Запись ActiveRecord-объектов в другой ActiveRecord-объект

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Maksat1991
Сообщения: 57
Зарегистрирован: 2016.10.16, 00:15

Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение Maksat1991 »

Есть таблица customer в БД. Поля id, name. На основе неё я создаю класс class Customer extends \yii\db\ActiveRecord {...}
Есть таблица orders в БД. Поля id, name, price, customer_id. На основе неё я создаю класс class Orders extends \yii\db\ActiveRecord {...}
Таблица orders связана с таблицей customer по полю customer_id, то есть Customer -> hasMany Orders.

Код: Выделить всё

class Customer extends \yii\db\ActiveRecord {
   ...
   public function getOrders()
    {
        return $this->hasMany(Orders::className(), ['customer_id' => id']);
    }
   ...
}
Когда я вытаскиваю какого-либо Customer из базы данных через $customer = Customer::findOne(1), то чтобы обратиться ко всем его заказам, то мне нужно выполнить $orders = $customer->orders;
Всё круто.

А теперь случай, когда я не буду тянуть свои объекты из БД, а буду создавать их сам, вот так:

Код: Выделить всё

$customer = new Customer();
$customer->name = "Maksat";
$order = new Order();
$order->name = "Покупка 1";
$order->price = 500;
$order2 = new Order();
$order2->name = "Покупка 2";
$order->price = 700;
$customer->orders[] = $order;
$customer->orders[] = $order2;
То я получу ошибку, потому что свойство Customer::orders - read only.
Я пытался в обход этой ошибки объявить внутри класса Customer новое свойство Customer::myorders, вот так:

Код: Выделить всё

class Customer extends \yii\db\ActiveRecord {
   public $myorders;
   ...
}
А потом делать

Код: Выделить всё

$customer->myorders[] = $order;
$customer->myorders[] = $order2;
Но тогда при

Код: Выделить всё

json_encode ($cusotmer)
моё объявленное вручную свойство $myorders не попадет в json-строку.
И при

Код: Выделить всё

$customer->toArray()
моё свойство тоже не попадет в массив.

Тогда как мне при создании этих двух объектов связывать их между собой? Мне надо их связать, но не сохранять в базу, а потом сделать json_encode, чтобы иметь в своей json-строке все связанные объекты тоже.
Последний раз редактировалось Maksat1991 2017.03.26, 10:05, всего редактировалось 5 раз.
Аватара пользователя
rodion_zlobin
Сообщения: 207
Зарегистрирован: 2017.01.11, 16:33

Re: Свойства в ActiveRecord-классе, объявленные вручную

Сообщение rodion_zlobin »

3 раза прочитал - 3 раза не понял. Сделай связь с таблицей ордеров и передавай это значение. Если у тебя в таблице клиенты поле ордеры, то используй BeforeSave
Аватара пользователя
rodion_zlobin
Сообщения: 207
Зарегистрирован: 2017.01.11, 16:33

Re: Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение rodion_zlobin »

Код: Выделить всё


$customer = new Customer();
$customer->name = "Maksat";
$customer->save();

$order = new Order();
$order->name = "Покупка 1";
$order->price = 500;
$order->customer_id = $customer->id;
$order->save();

$customer = findOne($customer->id);
$customer->orders;

Maksat1991
Сообщения: 57
Зарегистрирован: 2016.10.16, 00:15

Re: Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение Maksat1991 »

Я не могу сохранять в базу. Этот код я привел для примера, на самом же деле ко мне приходит большой xml-документ со 160 результатами поиска, который мне надо спарсить и превратить в мои объекты. Но не сохранять в базу, потому что за раз будет вставляться порядка 2000 строк во все таблицы, что очень замедлит работу сайта.
Почему я наследовал эти классы от ActiveRecord - потому что когда приходит другой xml-документ с заказом брони, то мне надо превратить его в те же объекты, но уже сохранить в базу. Но там всего 1 результат поиска бронируется, что при сохранении в базу несущественно по времени.

Пока что не нашел никакого другого выхода, кроме как сделать два раздельных класса, один наследованный от ActiveRecord, для брони.
И другой точно такой же, но не наследованный ни от чего.
Единственное, что может помочь сократить код при таком подходе - это трейты, вместо родительского класса. Но что-то я вызываю метод трейта из своего класса, а он ругается что не знает такого метода.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение zelenin »

ну то есть вам не нужен AR, а нужны обычные модели - простые php-классы, возможно dto, возможно реализующие JsonSerializable.
class Customer {} class Order {}
Maksat1991
Сообщения: 57
Зарегистрирован: 2016.10.16, 00:15

Re: Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение Maksat1991 »

В этих классах я реализовываю парсинг xml-документа в свои объекты. А потом когда я буду парсить другой xml-документ, то он будет парситься точно теми же методами. Но их уже надо будет сохранять в базу. Поэтому я использовал ActiveRecord.
Получается, я должен в двух разных классах писать одни и те же методы для парсинга, но один класс будет обычным, а другой ActiveRecord. Получится дублирование кода. Пока что только трейты придумал использовать чтобы избежать дублирования
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение zelenin »

Maksat1991 писал(а): 2017.03.27, 14:05 В этих классах я реализовываю парсинг xml-документа в свои объекты.
может хранение данных, а не парсинг?
Maksat1991 писал(а): 2017.03.27, 14:05 Получается, я должен в двух разных классах писать одни и те же методы для парсинга
методы для парсинга должны быть выделены в сервис, который будет парсить, а не в модели. Модели - это хранение данных.
Maksat1991 писал(а): 2017.03.27, 14:05но один класс будет обычным, а другой ActiveRecord. Получится дублирование кода.
разделение ответственностей. Дублирование кода - это о другом.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение zelenin »

код должен быть примерно таким:

Код: Выделить всё

$parser = new XmlParser;
$models = $parser->parse($xmlUrl); // получаем простые модели

// при необходимости делаем так
foreach ($models as $model) {
    $arModel = modelToArModel($model);
    ....
    $arModel->save();
}
в php 5.5+ я бы сделал так (для экономии памяти):

Код: Выделить всё

$parser = new XmlParser;
foreach ($parser->parse($xmlUrl) as $model) { // тут бы парсер вытаскивал по одной ноде xml и выплевывал бы через yield
    $arModel = modelToArModel($model);
    ....
    $arModel->save();
}
в 5.4 можно так же сделать, но через реализацию итератора (возможно оно из коробки есть).
Maksat1991
Сообщения: 57
Зарегистрирован: 2016.10.16, 00:15

Re: Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение Maksat1991 »

zelenin писал(а): 2017.03.27, 14:09
Maksat1991 писал(а): 2017.03.27, 14:05 В этих классах я реализовываю парсинг xml-документа в свои объекты.
может хранение данных, а не парсинг?
Maksat1991 писал(а): 2017.03.27, 14:05 Получается, я должен в двух разных классах писать одни и те же методы для парсинга
методы для парсинга должны быть выделены в сервис, который будет парсить, а не в модели. Модели - это хранение данных.
Maksat1991 писал(а): 2017.03.27, 14:05но один класс будет обычным, а другой ActiveRecord. Получится дублирование кода.
разделение ответственностей. Дублирование кода - это о другом.




1. Я создал класс со своими свойствами. В xml-документе приходят значения этих свойств, я в этом классе сделал метод parse, в нем создаю экземпляр своего же класса $model = new self(), извлекаю из xml-документа значения и присваиваю их свойствам своего объекта:
$model->name = $извлеченное_из_xmlки_значение_свойства;
...

Так как xml-документы в разных случаях приходят разные, а сущности одни и те же, то создал для каждой сущности свой базовый класс
BaseClass;
И для каждой xml-ки дочерние классы:
SearchClass extends BaseClass, и в нем один метод с парсингом xml-ки которая приходит в ответ на Search.
BookClass extends BaseClass, и в нем один метод с парсингом xml-ки которая приходит в ответ на Book.
и т.д.




2. В модели я данные храню, предварительно извлекая их из xml-ки.


3. По-моему будет дублирование, потому что здесь я вынужден создавать класс который extends ActiveRecord, и в нем продублировать некоторые методы, которые уже были в BaseClass.


Возможно, я неправильно создал структуру классов.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение zelenin »

1. модель не должна ничего парсить.

>>> Возможно, я неправильно создал структуру классов.

я вам написал правильную в предыдущем комменте
Maksat1991
Сообщения: 57
Зарегистрирован: 2016.10.16, 00:15

Re: Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение Maksat1991 »

А что означает "выделены в сервис"? Я не знаю что такое сервис. Отдельный класс?
Ок. А почему нельзя парсить данные в самом классе?
У меня в контроллер приходит xml-документ, который я превращаю в массив. И потом, чтобы получить из него объект своего класса, у меня есть такая модель с таким методом:
class User {
public $name;
public $email;
public $orders;
...
public static function setAttributes($params) {
$record = new self();
$record->name = $attributes['username'];
$record->email = $attributes['useremail'];
return self();
}
...
}

Получается, из контроллера я получаю данные, и чтобы получить объект своего класса, делаю так:
$user = User::setAttributes($params);


Правда, мой метод setAttributes внутри себя запускает еще метод Orders::setAttributes($order_params), и потом записывает массив полученных данных к себе вот так $record->orders = $orders;
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Запись ActiveRecord-объектов в другой ActiveRecord-объект

Сообщение zelenin »

Maksat1991 писал(а): 2017.03.29, 21:34 А что означает "выделены в сервис"? Я не знаю что такое сервис. Отдельный класс?
да
Maksat1991 писал(а): 2017.03.29, 21:34Ок. А почему нельзя парсить данные в самом классе?
https://ru.wikipedia.org/wiki/%D0%9F%D1 ... 1%82%D0%B8
Ответить