Обратиться к экземпляру объекта из другого класса

dubovsckij

Новичок
Привет всем.

Простите новичка, если вопрос прост или глуп.

У меня есть класс:

PHP:
class Pages {
   
    public $title;
    public $text;
   
    public function getPage()
    {
        $query = "SELECT * FROM `pages` WHERE `id`=1";
        $myrow = $db->select($query);
        if ($myrow!=false) {$num_rows = count($myrow);} else {$num_rows = 0; $myrow = 0;}
        return $myrow[0][text];
    }
}

В этом коде есть переменная $db, которая есть экземпляр класса подключения к базе данных.
Но у меня появляется ошибка " Call to a member function select() on a non-object ". Как я понимаю класс
Pages не может найти экземпляр класса $db и вызвать метод select.

До этого кода идет класс соединения с базой данных и потом создаю объект:
$db = DataBase::getDB();

Что я делаю не так?
 

Kotofey

FloodMaster.
Если у тебя DataBase синглтон тогда попробуй сделать в методе getPage() вызов того же метода ( или объяви в конструкторе)
и почитай про область видимости переменных.
 

Vano

Новичок
Ошибка твоя значит: не могу вызвать функцию select() из не обьекта. То-есть переменная, у которой пытаются вызвать метод select() - не обьект. То-есть $db это не обьект.
 

Vano

Новичок
Если ты не понял что я сказал, то читай: да про область видимости, и хотябы основы ООП
 

dubovsckij

Новичок
Если у тебя DataBase синглтон тогда попробуй сделать в методе getPage() вызов того же метода ( или объяви в конструкторе)
и почитай про область видимости переменных.
Спасибо за ответ. Так работает. Вот код:

PHP:
class Pages {
   
    public $title;
    public $text;
    public $db;
   
    public function __construct() {
        $this->db = DataBase::getDB();
    }
   
    public function getPage()
    {
        $query = "SELECT * FROM `pages` WHERE `id`=1";
        $myrow = $this->db->select($query);
        if ($myrow!=false) {$num_rows = count($myrow);} else {$num_rows = 0; $myrow = 0;}
        return $myrow[0][text];
    }
}
Но хотел спросить, неужели надо в каждом классе писать конструктор в котором надо создавать подключение к базе? Или это можно сделать как-то глобально, чтобы просто обращаться к базе?
 

Kotofey

FloodMaster.
Или в каждом конструктор, или сделай наследование от базового класса где единожды в конструкторе пропишешь.
к примеру
Model_Pages extends Model_Main, а в конструкторе Model_Main можешь прописать подключение к базе и создать другие вспомогательные методы
 

dubovsckij

Новичок
Т.е. мне лучше создать модель, где будет класс в котором будут храниться все вспомогательные методы и этот класс наследовать всеми классами через extends? И при надобности обращаться к любому методу из любого класса?
 

Kotofey

FloodMaster.
Т.е. мне лучше создать модель, где будет класс в котором будут храниться все вспомогательные методы и этот класс наследовать всеми классами через extends? И при надобности обращаться к любому методу из любого класса?
1. Да, только теми классами которые предназначены для работы с данными.
2. Не понял тебя.
 

dubovsckij

Новичок
1. Да, только теми классами которые предназначены для работы с данными.
2. Не понял тебя.
1) С данными из БД?
2) Я имел ввиду мне можно создать класс и там прописать абсолютно разные метода, например подключение к базе, получение настроек из БД и т.п.
 

Kotofey

FloodMaster.
1) С данными из БД?
2) Я имел ввиду мне можно создать класс и там прописать абсолютно разные метода, например подключение к базе, получение настроек из БД и т.п.
1.Да.
2. Почитай про ООП, под каждые цели создавай свои классы, с базой работает один класс, с конфигами второй.
 

fixxxer

К.О.
Партнер клуба
@dubovsckij, конкретно в твоем случае подойдет базовый класс с методом getDb(), который будет делать DataBase::getDB().

Но ты прав в наблюдении, что наследовать можно только один класс, и это так специально - если разрешить наследовать несколько (как в С++), легко получить помойку с конфликтами имен и так далее.
Поэтому чаще вместо наследования используют композицию. Вот, допустим, нам нужно работать и с базой, и с конфигом:

PHP:
class Pages {
   
    private $db;
    private $cfg;
   
    public function __construct(Database $db, ConfigReader $cfg) {
        $this->db = $db;
        $this->cfg = $cfg;
    }
   
    // ...

}
Тут зависимости передаются параметрами конструктора и хранятся в приватных свойствах, так что дальше в методах класса пишешь $this->db->..., $this->cfg->... и так далее.

Вручную создавать экземпляр класса Pages тогда придется так: new Pages($db, $cfg), причем $db и $cfg надо откуда-то взять. Это неудобно и громоздко, поэтому придумали такую штуку, как Dependency Injection и Service Containers. Вот, например, в фреймворке Symfony: http://symfony.com/doc/current/book/service_container.html, или в Laravel: https://laravel.com/docs/5.2/container.

Это очень удобный подход, являющийся де-факто стандартом, и я очень советую с ним ознакомиться и его изучить, но поначалу он может показаться сложным. В этом случае поначалу можешь остаться со своими синглтонами и писать в конструкторах код вроде того, что ты уже написал, и комбинировать решения с наследованием и с присваиваниями в конструкторе. Скажем, если есть набор классов, где основная задача - работа с базой данных, но в каком-то из них может понадобиться еще и конфиг, то выдели базовый класс с методом getDb(), а там, где понадобился конфиг, напишешь что-то вроде $this->cfg = Config::getInstance() конструкторе. С опытом все равно придешь к Dependency Injection.
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
PHP:
class Foo {
    private $bar;
    function __construct($bar) {
        $this->bar = $bar;
    }
}

class Sl{
    private $services = [];
    function __construct( $services ){
        $this->services = $services;
    }
    function __get( $service ){
        return (
            in_array($service, array_keys($this->services))
            ? (
              is_callable( $this->services[$service] )
              ? call_user_func_array( $this->services[$service], array($this))
              : $this->services[$service]
           ) : null
        );
    }
}

print_r((new Sl([
    'bar' => ['hello' => 'world'],
    'Foo' => function(Sl $sl){
        return new Foo( $sl->bar );
    },
]))->Foo);
 
Последнее редактирование:

dubovsckij

Новичок
@dubovsckij

Вручную создавать экземпляр класса Pages тогда придется так: new Pages($db, $cfg), причем $db и $cfg надо откуда-то взять. Это неудобно и громоздко, поэтому придумали такую штуку, как Dependency Injection и Service Containers. Вот, например, в фреймворке Symfony: http://symfony.com/doc/current/book/service_container.html, или в Laravel: https://laravel.com/docs/5.2/container.

Это очень удобный подход, являющийся де-факто стандартом, и я очень советую с ним ознакомиться и его изучить, но поначалу он может показаться сложным. В этом случае поначалу можешь остаться со своими синглтонами и писать в конструкторах код вроде того, что ты уже написал, и комбинировать решения с наследованием и с присваиваниями в конструкторе. Скажем, если есть набор классов, где основная задача - работа с базой данных, но в каком-то из них может понадобиться еще и конфиг, то выдели базовый класс с методом getDb(), а там, где понадобился конфиг, напишешь что-то вроде $this->cfg = Config::getInstance() конструкторе. С опытом все равно придешь к Dependency Injection.
Спасибо за такой подробный ответ. Хочу сразу научится правильно. Дело в том что много лет писал на php с помощью процедурного подхода, так что я не совсем чайник. Знания ООП небольшие, хочу наверстать. Хочу изучить Ларавел и при изучении столкнулся сразу с рядом трудностей и решил закрыть пробелы в знаниях. Скажите, пожалуйста, Вы говорили за Service Containers в Laravel, Вы имеете ввиду интерфейсы?
 

fixxxer

К.О.
Партнер клуба
Когда я говорил про service containers, я имел ввиду service containers (вон я ссылочку там дал).

Интерфейсы сюда имеют косвенное отношение. Когда контейнер при создании экземпляра объекта разрешает зависимости (то есть определяется, какие аргументы передать в конструктор создаваемого класса), он ищет в своей конфигурации совпадение по тайпхинтам в конструкторе, если при этом надо по этому совпадению создать еще другой экземпляр - идет далее рекурсивно. Обычно используют именно интерфейсы с целью ослабления жестких связей в коде - с интерфейсами всегда можно изменить конкретную реализацию интерфейса на какую-то другую его реализацию, и изменить будет достаточно только конфигурацию контейнера. Но можно использовать и имена классов.
 
Сверху