Как виртуализировать поля в ActiveRecord ?

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

Делаю REST-сервис на Yii.
PHP вообще, и Yii в частности знаю пока слабовато, отсюда и вопросы (возможно глуповатые - не обессудьте... чем богаты.... :))
Теперь о проблеме:
Хотелось бы, чтоб мои наследники ActiveRecord имели чуть больше полей, чем есть в таблицах базы... :)
Т.е. замысел в том, чтоб создать "вычислимые поля".
Например (пример вполне реальный), в таблице 'resources' базы есть поля:
1) 'n' (краткое название некоего ресурса),
2) 'fn' (полное название некоего ресурса),
притом поле 'n' обязательное (required), а вот 'fn' может быть и пустым.
Мне хотелось бы завести в Resources (extends \yii\db\ActiveRecord) для некоторых GET-запросов поле
3) 'name'
В базе его нет и не надо, оно вычислимое и должно вычисляться примерно таким способом (типа псевдокод, приближенный к реальности):

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

   public function getName()
   {
     return  (this->fn == "")  ?  n : fn;
   }
т.е. при наличии значения в 'fn' возвращается оно, при отсутствии - значение 'n'

Как реализовать подобные вычислимые (доступные только для чтения) поля проще всего ?
// Уверен что "в принципе" это возможно.
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

Йессс!!
Ещё не пробовал, но принцип ясен. Спасибо!
// Отпишусь, когда заработает.
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

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

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

   public function fields() {
      $fields = parent::fields();
      $adds = ['name' => function() {
               return ($this->fn=='') ? $this->n : $this->fn;
            }];

      return $fields + $adds;
   }
получаю "500 Internal Server Error"
с расшифровкой: Column not found: 1054 Unknown column 'name' in 'field list'↵The SQL being executed was: SELECT `n`, `fn`, `name`
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение zelenin »

в принципе выглядит правильно, кроме код-стайла и логики.

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

   public function fields() {
        $fields = parent::fields();
        $adds = [
            'name' => function() {
                    return empty($this->fn) ? $this->n : $this->fn;
                }
        ];
        return $adds + $fields; // именно так, иначе сработает поле name из $fields
    }
 
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

zelenin писал(а):в принципе выглядит правильно, кроме код-стайла и логики.

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

   public function fields() {
        $fields = parent::fields();
        $adds = [
            'name' => function() {
                    return empty($this->fn) ? $this->n : $this->fn;
                }
        ];
        return $adds + $fields; // именно так, иначе сработает поле name из $fields
    }
Переделал:

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

   public function fields() {
      $fields = parent::fields();
//      $adds = [
//         'name' => function() {
//               return ($this->fn=='') ? $this->n : $this->fn;
//            },
//      ];
      $adds = [
         'name' => function() {
               return empty($this->fn) ? $this->n : $this->fn;
            },
      ];

      return $adds + $fields;
   } 
таки не работает :(
Возвращается в точности такая же ошибка...
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение zelenin »

ну это понятно, ничего конкретно-то не поменялось.. дебажьте. У меня работает такой подход - только что проверил.

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

    public function fields()
    {
        return [
            'id', 'username', 'role',
            'status' => function () {
                    return $this->getStatus($this->status);
                },
            'status2' => function () {
                    return $this->getStatus($this->status);
                },
        ];
    }
 

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

[{"id":1,"username":"developer","role":"developer","status":"Active","status2":"Active"},{"id":4,"username":"zelenin","role":"developer","status":"Active","status2":"Active"}]
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

Во, блин.
Вапчета я могу виртуализировать поля на JS на клиенте.
// angular рулит. я уже сделал несколько фильтров-виртуализаторов. всё работает.

Но. Во первых, там несколько больше писанины получается, и к тому же сразу в нескольких местах (везде, где потребуются виртуальные значения).
А во вторых: делать это придётся на всех клиентах, юзающих данный сервис, а этим REST-сервисом уже собираются пользоваться как минимум три клиентских проекта (один из них делаю я). А потому хотелось бы решить проблему один раз, и централизованно....
... короче, "хелп ми, плиз"... подайте идею христа ради... :) В вышеуказанном синтаксисе схема не пашет. Возможно какие-то изменения в интерфейсе? Всё-таки бета-версия. Я уже сталкивался с устаревшей информацией в справке....
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение zelenin »

1. я уже сказал, дебажьте.
2. не используем бету, используем последнюю дев-версию.
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

zelenin писал(а):ну это понятно, ничего конкретно-то не поменялось.. дебажьте. У меня работает такой подход - только что проверил.

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

    public function fields()
    {
        return [
            'id', 'username', 'role',
            'status' => function () {
                    return $this->getStatus($this->status);
                },
            'status2' => function () {
                    return $this->getStatus($this->status);
                },
        ];
    }

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

[{"id":1,"username":"developer","role":"developer","status":"Active","status2":"Active"},{"id":4,"username":"zelenin","role":"developer","status":"Active","status2":"Active"}]
Ок. Буду грызть дальше.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение zelenin »

обновитесь с джитхаба
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

zelenin писал(а):обновитесь с джитхаба
Композер говорит типа "нечего обновлять" // если я правильно интерпретировал
> composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files


Может чего не так делаю? // Yii2 (basic) устанавливал 17.08.2014
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение zelenin »

"yiisoft/yii2": "*", - последняя дев-версия, не бета.
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

zelenin писал(а):"yiisoft/yii2": "*", - последняя дев-версия, не бета.
Ну да. Оттуда и обновлялся.

Короче. Похоже дебажить нужно не мне. :) :)
Эта штука

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

  public function fields() {
      $fields = parent::fields();
      $adds = [
         'name' => function() {
               return empty($this->fn) ? $this->n : $this->fn;
            },
      ];
      return $adds + $fields;
   }
 
- она работает. Но не так как ожидалось.
В Select'e поле 'name' указывать нельзя - приводит к вышеуказанной ошибке.
Зато при любых других полях в Select'e, это поле добавляется как "бесплатный довесок".
Пример: // Это геттер для реляции в другой модели

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

   public function getPamjatnik()
   {
      return $this->hasOne(Pamjatnik::ClassName(), ['id' => 'id_pamjatnik'])->select('n,fn');
   } 
Ответ сервера (фрагмент):

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

  ......   
    "id_tip_zdanija": 1,
    "id_pamjatnik": 6,
    "Tolshhina_sten": "28.00",
    "Kolichestvo_jetazhej": 3,
    "Ploshhad": "232.00",
    "tip_zdanija": {
      "id": 1,
      "n": "Храм"
    },
    "pamjatnik": {
      "name": "Большой успенский собор",
      "n": "Успенский собор",
      "fn": "Большой успенский собор"
    }
  } 
Как видим поле правильно вычисляется и доставляется в качестве extraField. Но что характерно - без всякой просьбы. :)
Чешу репу. Привыкать к этому или таки ждать исправлений?
Последний раз редактировалось MetaDriver 2014.08.31, 12:07, всего редактировалось 2 раза.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение zelenin »

это не экстра-филд, а филд, соответственно без доп. запроса выведется.
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

zelenin писал(а):это не экстра-филд, а филд, соответственно без доп. запроса выведется.
для той другой модели, для которой геттер с "селект"ом прописаны - это таки экстра-филд.
там полей (филдов) в этом самом "памятнике" вапчета 100500.
Вот полный расклад:

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

   public function fields() {
      $fields = [
         'id',
         'n',
         'fn',
         'id_kompleks_pamjatnikov',
         'id_kategorija_pamjatnika',
         'id_obshhij_tip_pamjatnika',
         'id_tip_pamjatnika',
         'id_podtip_pamjatnika',
         'id_kategorija_ohrany',
         'id_harakter_sovremennogo_ispolzovanija',
         'id_tehnicheskoe_sostojanie',
         'id_region',
         'id_rajon',
         'id_naseljonnyj_punkt',
         'id_ulica',
         'Nomer_dokumenta_o_postanovke_na_uchet',
         'Ploshhad',
         'Datirovka',
         'Opisanie_mestopolozhenija',
         'Nomer_doma',
         'name' => function() {
               return empty($this->fn) ? $this->n : $this->fn;
            },
      ];
      return $fields;
   }
и другие поля так нагло себя не ведут - только наше любимое вычислимое поле 'name' ... :)
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение zelenin »

Определитесь, что такое филдс и экстра-филдс, и все встанет на свои места.
Экстра-филдс - это связи модели, которые можно развернуть. Филдс - просто атрибуты модели, возвращаемые при выборке.
В вашем примере

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

...
"pamjatnik": {
      "name": "Большой успенский собор",
      "n": "Успенский собор",
      "fn": "Большой успенский собор"
    }
...
 
pamjatnik - экстра-филд (связь),
name, fn, n - филдс.
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

zelenin писал(а):Определитесь, что такое филдс и экстра-филдс, и все встанет на свои места.
Экстра-филдс - это связи модели, которые можно развернуть. Филдс - просто атрибуты модели, возвращаемые при выборке.
В вашем примере

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

...
"pamjatnik": {
      "name": "Большой успенский собор",
      "n": "Успенский собор",
      "fn": "Большой успенский собор"
    }
...
pamjatnik - экстра-филд (связь),
name, fn, n - филдс.
Это я понимаю. Просто вызыватся этот pamjatnik в моём случае из другой модели, в качестве реляции.
// Я думаю мы друг-друга наконец поняли. (Оба правы :)) Просто я невнятно выразился поначалу. Кстати, большое Вам спасибо за содействие.
Аватара пользователя
MetaDriver
Сообщения: 43
Зарегистрирован: 2014.04.21, 20:53
Откуда: Казань

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение MetaDriver »

Вот, кстати. При таком (полном) определениии fields() в объекте pamjatnik

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

   public function fields() {
      $fields = [
         'id',
         'n',
         'fn',
         'id_kompleks_pamjatnikov',
         'id_kategorija_pamjatnika',
         'id_obshhij_tip_pamjatnika',
         'id_tip_pamjatnika',
         'id_podtip_pamjatnika',
         'id_kategorija_ohrany',
         'id_harakter_sovremennogo_ispolzovanija',
         'id_tehnicheskoe_sostojanie',
         'id_region',
         'id_rajon',
         'id_naseljonnyj_punkt',
         'id_ulica',
         'Nomer_dokumenta_o_postanovke_na_uchet',
         'Ploshhad',
         'Datirovka',
         'Opisanie_mestopolozhenija',
         'Nomer_doma',
         'name' => function() {
               return empty($this->fn) ? $this->n : $this->fn;
            },
      ];
      return $fields;
   }
 
в том же самом реляционном запросе (... return $this->hasOne(Pamjatnik::ClassName(), ['id' => 'id_pamjatnik'])->select('n,fn'); ...),
сервер возвращает

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

    "Tolshhina_sten": "28.00",
    "Kolichestvo_jetazhej": 3,
    "Ploshhad": "232.00",
    "tip_zdanija": {
      "id": 1,
      "n": "Храм"
    },
    "pamjatnik": {
      "id": null,
      "n": "Успенский собор",
      "fn": "Большой успенский собор",
      "id_kompleks_pamjatnikov": null,
      "id_kategorija_pamjatnika": null,
      "id_obshhij_tip_pamjatnika": null,
      "id_tip_pamjatnika": null,
      "id_podtip_pamjatnika": null,
      "id_kategorija_ohrany": null,
      "id_harakter_sovremennogo_ispolzovanija": null,
      "id_tehnicheskoe_sostojanie": null,
      "id_region": null,
      "id_rajon": null,
      "id_naseljonnyj_punkt": null,
      "id_ulica": null,
      "Nomer_dokumenta_o_postanovke_na_uchet": null,
      "Ploshhad": null,
      "Datirovka": null,
      "Opisanie_mestopolozhenija": null,
      "Nomer_doma": null,
      "name": "Большой успенский собор"
    }
  }
 
Что вапче-та смахиват на весьма конкретный баг. // Особенно учитывая, что поля, возвращённые со значением 'null' в базе отнюдь не пустые.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Как виртуализировать поля в ActiveRecord ?

Сообщение zelenin »

вы же не выбираете эти поля в запросе, вот они и нулевые - ->select('n,fn')
Ответить