понедельник, 2 мая 2011 г.

Связывание таблиц в Yii

Следующая проблема, с которой я столкнулся, это связывание таблиц. Товары принадлежат определенной категории, и надо, чтобы вместо индекса категории, который хранится в таблице tbl_assortiment показывалось бы имя категории из таблицы tbl_category.
Делал это так: сначала описал связи в моделях assortiment и category
В файле Z:\home\catalog.loc\www\protected\models\Assortiment.php

/**
* @return array relational rules.
*/
 public function relations()
{
 return array(
'category' => array(self::BELONGS_TO, 'Category', 'category_id'),
 );
}
В файле Z:\home\catalog.loc\www\protected\models\Category.php
/**
* @return array relational rules.
*/
public function relations()
{ 

return array(
'assortiment'=>array(self::HAS_MANY, 'assortiment', 'category_id'),
);
}
Но это еще не всё. Теперь надо в файлах представлений написать так:
В файле Z:\home\catalog.loc\www\protected\views\assortiment\view.php
$this->widget('zii.widgets.CDetailView', array(
'data'=>$model,
'attributes'=>array(
'id',
'name',
'description',
array(
'label'=>'Category',
'type'=>'raw',
'value'=>$model->category->name,
),
),
));
Теперь при выборе конкретного товара его категория отображается словами.
А чтобы при действии admin в таблице с сортировкой и поиском тоже показывались бы названия категорий словами надо подправить элемент CGridView в файле Z:\home\catalog.loc\www\protected\views\assortiment\admin.php
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'assortiment-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
'name',
array(
'name' => 'category_id',
'filter' => CHtml::listData(Category::model()->findAll(), 'id', 'name'),
'value' => '$data->category->name',
),
'shown',
 array(
'class'=>'CButtonColumn',
),
),
));
Теперь поиск и сортировка работают!
Осталось изменить файл Z:\home\catalog.loc\www\protected\views\assortiment\_form.php выбор категории нужен в виде выпадающего списка из существующих категорий.
В том месте, где сейчас выводится просто текстовое поле категория следует заменить код на
<?php
echo $form->dropDownList($model,'category_id', 
Category::All(),
array('empty' => '(Select a category)')
);?>       
<?php echo $form->error($model,'category_id'); ?>
</div>
Тут я использовал действие Category::All() оно возвращает список всех категорий. Его описание хранится в файле:
Z:\home\catalog.loc\www\protected\models\Category.php в любом месте добавляем:
/**
* Returns the list of all category.
* @return list data of all category
*/
public static function All()
{
$models = Category::model()->findAll(
array('order' => 'name'));
$list = CHtml::listData($models,
'id', 'name', 'shown');
return $list;
}

Теперь таблицы связаны, и везде, где это надо вместо индекса категории выводится имя категории. В следующий раз расскажу, как делал скрытие категорий и товаров (поле shown в обоих таблицах должно скрывать категорию или товар)
Продолжение следует.

47 комментариев:

  1. уважаемый автор, мне очень интересен ваш блог, но я не всегда могу разобраться что за код и где его надо править. Например, здесь:

    labelEx($model,'category_id'); ?>
    dropDownList($model,'category_id',
    Category::All(),
    array('empty' => '(Select a category)')
    );?>
    error($model,'category_id'); ?>

    совершенно не понятно что и откуда берется, потому что нет открывающих тегов.
    Не могли бы вы выложить полный код файла /чего-то там/create.php

    ОтветитьУдалить
  2. Егор, хорошо, что написали! Там у меня оказалась ошибка: эта часть кода, конечно же в представлении. Файл Z:\home\catalog.loc\www\protected\views\assortiment\_form.php Я уже исправил в сообщении.
    Могу заодно прокомментировать этот код:
    views\assortiment\_form.php файл, формы, куда пользователь вводит данные, чтобы измениь или добавить товар.
    dropDownList($model,'category_id',
    Category::All(),
    array('empty' => '(Select a category)')
    );?>
    Это выпадающий список, сзязанный с моделью $model, когда пользователь выберит, данные запишутся в модель в поле category_id.
    Category::All(),
    array('empty' => '(Select a category)'
    Это строки списка в виде массива: функция Category::All() возвращает список всех категорий, а 'empty' => '(Select a category)' это название пункта, когда ничего не выбрано.
    error($model,'category_id'); ?> Выводит ошибку ввода, если такая случается

    ОтветитьУдалить
  3. >>Теперь поиск и сортировка работают!

    - не работают ((

    $this->widget('zii.widgets.grid.CGridView', array(
    'id'=>'assortiment-grid',
    'dataProvider'=>$model->search(),
    'filter'=>$model,
    'columns'=>array(
    'id',
    'name',
    array(
    'name' => 'category_id',
    'filter' => CHtml::listData(Category::model()->findAll(), 'id', 'name'),
    'value' => '$data->category->name',
    ),
    'shown',
    array(
    'class'=>'CButtonColumn',
    ),
    ),
    ));


    данные беруться из
    'value' => '$data->category->name',

    а сортировка идет по:
    'name' => 'category_id',

    ну а фильтр понятно работать будет
    'filter' => CHtml::listData(Category::model()->findAll(), 'id', 'name'),

    ОтветитьУдалить
    Ответы
    1. Я тут не совсем понял, что у меня не так?

      Удалить
    2. вместо 'value' => '$data->category->name',
      надо писать 'value' => '$model->category->name',

      Удалить
  4. Идея отличная рассказать про yii доступным языком, но как то не особо доступно получается. Хотелось бы, чтоб в коде, каждая строка была прокомментирована...

    ОтветитьУдалить
    Ответы
    1. Ну каждую то, наверное не надо :) Ну если надо, всегда прокомментирую в комментариях!

      Удалить
  5. Этот комментарий был удален автором.

    ОтветитьУдалить
  6. У меня возникла проблемка, но я ее решил, но почему она возникла я не понял. Когда добавляешь новую категорию, в форме два поля textarea 'Name','Description' и одно поле textfield, которое, не знаю почему, имеет size="0" maxlength="0", ничего не трогал, шел в точности по Вашему алгоритму.
    Теперь я просто заменил в файле protected\views\category\_form.php
    textField($model,'show',array('size'=>0,'maxlength'=>0)); на
    textField($model,'show',array('size'=>0,'maxlength'=>3));.

    вроде работает, есть идея почему проблема возникла?

    ОтветитьУдалить
    Ответы
    1. Не знаю, у меня такого поля нет, а какое у него имя?

      Удалить
    2. show

      Да в принципе неважно, самое главное работает
      Спасибо.

      Удалить
  7. смотрю блог давно написан и версии поменялись и все такое, делал по этому блогу свой сайтик и при связе БДшек была проблема, то как ты изменил вьюшку оно поменяло только при открытии одного файла, т.е. писало имя нужной котегории, а вот если открывать список данных т.е. нажать кнопку List оно все равно выводило id, а не категорию. Так что прошу автора так же добавить 4-ым скрином изменение файла
    Z:\home\catalog.loc\www\protected\views\assortiment\_view.php

    З.Ы. мысль свою написал криво, но времени нет составлять красиво мысль :)

    ОтветитьУдалить
    Ответы
    1. В \_view.php

      <b><?php echo CHtml::encode($data->getAttributeLabel('category_id')); ?>:</b>
      <?php echo CHtml::encode($data->category->{name_.Yii::app()->language}); ?>
      <br />

      Удалить
    2. а можно вот здесь поподробнее, а то что-то не получается. Поменял в файле Z:\home\catalog.loc\www\protected\views\tblAssortiment\_view.php

      getAttributeLabel('category_id')); ?>:
      category_id);?>



      на ваш код

      getAttributeLabel('category_id')); ?>:
      category->{name_.Yii::app()->language}); ?>



      чё-то ругается.

      Я новичок, так что не судите строго!)

      Удалить
    3. я просто вставил echo $data->category->name и всё заработало.

      Удалить
  8. Очень актуальная статья! Спасибо!

    ОтветитьУдалить
  9. почему при вводе этой строки только с моими таблицами 'value' => '$data->shahtu->name' выдает ошибку, что Property "Analysis.shahtu" is not defined?

    ОтветитьУдалить
    Ответы
    1. Вероятно, поле shahtu не описано в связях.

      'shahtu' => array(self::BELONGS_TO, 'Другая модель', 'поле модели shahtu с ключем другой модели'),

      Удалить
  10. я сейчас пытаюсь как попугай за вами повторять, но почему-то вот здесь вылетает у меня ошибка
    'filter' => CHtml::listData(Category::model()->findAll(), 'id', 'name'
    То есть, категории отображаются в списке, но когда пытаюсь их выбрать ошибку выдает, даже не на страничке браузера, а выпадающим окном в нем вот такое(привожу не все)"Error 500: ..."
    В логах тоже ничего полезного апач не записал, для меня так точно.( Я думаю может из-за того что у меня версия фреймворка другая, и уже там что-то поменяли, пытался гуглить - пока безрезультатно. Не подскажите? За статьи отдельное спасибо, большой труд проделан.

    ОтветитьУдалить
    Ответы
    1. На в ряд ли из-за версии.
      Тут никакой ошибки быть не должно..

      array(
      'name' => 'category_id',
      'filter' => CHtml::listData(Category::model()->findAll(), 'id', 'name_'.Yii::app()->language),
      'value' => '$data->category->name_'.Yii::app()->language,
      ),

      Удалить
    2. да, спасибо, вы правы у меня ошибка была, знак доллара не поставил перед data. До этого в питоне ковырялся с Django, дебаг получше как мне показалось, на такого рода ошибку сразу интерпретатор укажет на строку, в которой ошибка.

      Удалить
  11. Во-первых спасибо! Очень многое на свои места стало (:
    Ну а во-вторых вопросик... Можно ли связать с помощью BELONGS_TO и HAS_MANY поля одной таблицы? Пытаюсь сделать многоуровневое меню, но что-то пока никак :(

    ОтветитьУдалить
    Ответы
    1. 'parent'=>array(self::BELONGS_TO, 'category', 'cat_parent'),

      Удалить
  12. А можно увидеть структуру таблиц?

    ОтветитьУдалить
    Ответы
    1. Сделал новым постом: http://psyhos.blogspot.com/2012/10/blog-post.html

      Удалить
  13. СПС!Блин а у меня не пашет(
    'assortiment'=>array(self::HAS_MANY, 'assortiment', 'category_id'),
    что значит category_id это поле с таблицы assortiment, а к какому тогда полю таблицы Category оно привязывается?

    ОтветитьУдалить
    Ответы
    1. Почему не пашет не знаю, надо разбираться с самого начала - проверить, что у вас есть..
      Поле category_id действительно из таблицы assortiment, оно привязано к полю ID таблицы Category

      Удалить
    2. А где это описано? )

      Удалить
  14. 'category' => array(self::BELONGS_TO, 'Category', 'category_id'), тут тоже category_id это поле из таблицы assortiment или уже это таблица Category?

    ОтветитьУдалить
    Ответы
    1. Это пишется в модели assortiment. В этой модели создается поле category (которого в таблице нет, есть только category_id) из этого созданного в связях поля category можно взять имя категории: category->name_ru

      Удалить
    2. А где тогда описывается то что мы привязываем поле category_id assortiment таблицы к полю ID таблицы category?

      Удалить
    3. В перых строках этого поста:
      цытата:"
      В файле Z:\home\catalog.loc\www\protected\models\Assortiment.php

      /**
      * @return array relational rules.
      */
      public function relations()
      {
      return array(
      'category' => array(self::BELONGS_TO, 'Category', 'category_id'),
      );
      }
      "

      Удалить
    4. Спасибо за потраченное время.У меня все равно не работает выдает ошибку: Не определено свойство "Posts.vechic".
      У меня есть две таблицы одна таблица posts а другая vechicle_type!Надо связать поле vechicle_type таблицы posts с
      таблицей vechicle_type немного запутанно потому что имя таблицы и поля одинаковое может из за этого и не работает.
      Я делаю так в models\Posts.php пишу
      public function relations() {
      return array(
      'vechic' => array(self::BELONGS_TO, 'VechicleType', 'vechicle_type'),
      );
      }
      В models\VechicleType.php пишу
      public function relations() {
      return array(
      'posts' => array(self::HAS_MANY, 'Posts', 'vechicle_type')
      );
      }
      И наконец в views\posts\admin.php так
      $this->widget('zii.widgets.grid.CGridView', array(
      'id'=>'posts-grid',
      'dataProvider'=>$model->search(),
      'filter'=>$model,
      'columns'=>array(
      'id',
      'user_id',
      array(
      'name'=>'vechicle_type',
      'value' =>'$data->vechic->vechicle_type',
      ),
      array(
      'class'=>'CButtonColumn',
      ),
      ),

      Удалить
  15. Все уже работает по собственной глупости случайно залил модель posts
    не туда проблему решил спс. Все работает))

    ОтветитьУдалить
  16. Если не сложно - для такого случая

    Create table users {
    id
    name
    city_id
    }
    Create table city {
    id
    city_name
    }

    Для relations() return array();
    в Users.php
    'city_name' => array(self::BELONGS_TO, 'City', 'city_id'),
    в City.php
    'Users'=>array(self::HAS_MANY, 'Users', 'id'),


    в представлении users/admin пытаюсь сделать
    'value'=>'$model->city_name',

    И пустой столбец.
    Укажите пожалуйста на ошибки.

    ОтветитьУдалить
    Ответы
    1. 'value'=>'$model->city_name->name',

      Удалить
    2. Тоже пусто, вывожу var_dump($model) - NULL в ячейках..

      Сорри, я новичок в Yii, только на днях начал вникать.

      Удалить
  17. Этот комментарий был удален автором.

    ОтветитьУдалить
  18. Дмитрий у вас хороший блог (с целью) уже год вы этим занимаетесь и делитесь цветом с серыми массами в этом деле. Есть много успешных частей в статьях, которые действительно достаточно ясны и понятны с первого взгляда. Но хотелось бы какой то лучшей организации статей т.к начиная с самого начала можно увидеть вашу цель к которой шли изначально, а затем ее не своевременную поэтапную реализацию, тем самым привожу пример: 1я статья графическая структура БД (при виде которой некоторые сразу сдаются) затем установка Yii чрез некоторое время (после настройки конфига мейн) работаем с таблицами и моделями от этих таблиц (не смотря на то что часть отсутствует) + gii на тот момент никто не видел но он уже подключен =)) Зная вашу любовь к коментам все же находим дамп БД и все упорядочивается (Хотя опять не понятны некоторые листинги которые не имеют начала типа <?php а идут сразу с воздуха позже где то после нескольких ошибок становится вновь ясно откуда это…) Извините но так не делается Даже на Офф. Переводе по созданию приложения все четко поэтапно идет как по плану. И главное с поправками что куда и зачем пишется.
    P.S
    У вас эти моменты увы опущены… А так все хорошо начиналось.(Может вам помочь, хотя б с первыми 2-мя которые я понял))) просто покажу что имею в виду, и вы меня поймете.

    ОтветитьУдалить
    Ответы
    1. Ivan Oleinik, вы как то невнимательно читали, в этом посте (http://psyhos.blogspot.com/2011/04/yii.html) и подключение gii и генерация с помощью него кода. В каждом посте я описываю свои действия и каждый последующий пост отталкивается от предыдущего. Так же, по ходу повествования изменяеются и таблицы и сам код я об этом и пишу. Конечно, мой скромный блог и не притендует на роль исчерпывающего руководства, если вы из него хоть что ни будь полезное узнали, я уже рад!

      Удалить
  19. Я извиняюсь но быть может я не внимательный и прочитав все предыдущие стати чего то не заметил, но поиск по словам chrome достаточно точный.

    Давайте рассуждать логически:
    Каждая статья имеет дату ее создания. Месяца идут с момента первой Апрель-> Май-> Июнь ->Июль и.т.д Числа от 1-го до 31-го.
    Начиная с первой:
    (1) с чего я начал. установка yii framework - четверг, 28 апреля 2011 г.)
    (2) проектирование первого сайта на yii - пятница, 29 апреля 2011 г.)
    (3) связывание таблиц в yii - понедельник, 2 мая 2011 г.)
    Так как находимся именно в этой статье ее и рассматриваем:
    Ничего пока про gii не слышали но уже работаем с \models\ и \views\ с какими то представлениями и моделями, сразу возникал вопрос а где же момент создания этих шаблонов…
    (4) скрытие категорий и товаров - вторник, 10 мая 2011 г.)
    (5) изменение меню - вторник, 10 мая 2011 г.)
    (6) создание меню пользователя (zii.widgets.cmenu) - понедельник, 16 мая 2011 г.)

    (7) настройка файла main.php в yii. основная конфигурация - четверг, 26 мая 2011 г.) первое упоминание про генератор gii (но пока мы его только подключили…)

    Пропускаю еще 3 стати и попадаю в следующий месяц Июнь!
    (11) генерация кода модуля в gii - понедельник, 27 июня 2011 г.) первая генерация через gii

    P.S
    Я может как то не правильно считаю/рассуждаю... Но по датам все именно так.

    ОтветитьУдалить
    Ответы
    1. Спасибо за такой подробный анализ.
      В пункте (2) я как раз и говрю о том, как я подключил gii и что я сгенерировал с помощью него структуру сайта с моими таблицами. Поривожу цитату: "..И активировал GII генератор кода (В том же файле main.php). О том, что это надо сделать именно так, и зачем это нужно узнал из мануалов, там про это хорошо сказано....В мануале хорошо описано как пользоваться этим GII. С помощью него я создал модели, контроллеры и CRUD...."
      В пункте (3) я как раз связываю таблицы, так как они уже есть и есть сгенерированная основа (работаем с \models\ и \views\).
      В пункте (11) я пользуюсь gii для генерации модуля. Второй раз использую gii. Первый раз в пункте (2) когда я генерил саму основу сайта.


      Удалить
    2. В процессе я то как раз это понял, вопрос в том, откуда ж мне было знать как им пользоваться если б я ранее не знал про переводы Александра Макарова и Sam Dark.

      Например представления я так и не научился генерировать так же само как и формы т.к там классы указываются а я в них путаюсь, за то модели сделал=)

      Раскройте тайну с какими полями их создавать (все)
      А еще лучше дополните пост.
      assortiment\view.php
      \views\assortiment\admin.php ...
      P.S
      Спасибо! )

      Удалить
  20. Так что? Про представления чего то расскажете? Я не думаю что там нужно будет много печатать)

    ОтветитьУдалить