Yii2. Знакомство

    Введение

    На днях, свершилось событие, которое Я и думаю еще немало людей ждали. Авторы Yii Framework выкатили превью-версию.

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

    Начало

    Вторая версия отличается от первой кардинально. Список в краткой форме:

    — Отделили ядро от дополнений. Выбросили много классов. Часть из них перекочуют в отдельные, официально поддерживаемые, расширения. Часть просто убрана за ненадобностью.

    — Базовый CComponent разделили на Object и Component. Первый осуществляет работу геттеров и сеттеров, второй расширяя первый, добавляя события и поведения.

    — Видоизменилось подключение событий и поведений. Подписываемся на событие
    $post->on('update', function($event) {
        // send email notification
    });
    

    Настраиваем компонент
    $component = \Yii::createObject(array(
      'class' => '\app\components\GoogleMap',
      'apiKey' => 'xyz',
      // Добавим событие
      'on eventName' => array('Event', 'run'),
      // Добавим поведение
      'as behaviorName' => array(/* Behavior config */),
    ));
    

    — Добавили новый класс View, теперь у нас настоящий MVC фреймворк. Представление
    <?php
    use yii\helpers\base\Html;
    /**
     * Обратите внимание $this в представлении это уже не контроллер.
     * Добраться к контроллеру можно $this->context
     * @var yii\base\View $this
     */
    $this->title = 'Hello world';
    ?>
    <h1><?php echo Html::encode($this->title); ?></h1>
    <p class="lead">Привет мир!</p>
    
    * View можно для каждого контроллера устанавливать, или использовать базовый для приложения.

    — render() контроллера больше ничего не выводит. Оно возвращает данные
    public function actionIndex()
    {
    	echo $this->render('index');
    }
    

    — В контроллере появились два события, на котрые можно подписываться: beforeAction, afterAction
    public function init()
    {
      $this->on('beforeAction', function($event) {
        // отменяем действие
        $event->isValid = false;
      });
    }
    

    — Убраны фильтры контроллера CFilter, теперь все делается через поведения
    public function behaviors()
    {
      return array(
        'AccessControl' => array(
          'class' => '\yii\web\AccessControl',
          'rules' =>array(/* тут ничего не поменялось */),
        ),
      );
    }
    

    — В контроллере появился отличный помощник — метод populate
    public function actionLogin()
    {
      $model = new LoginForm();
      if ($this->populate($_POST, $model) && $model->login()) {
        Yii::$app->response->redirect(array('site/index'));
      }
    
      echo $this->render('login', array(
        'model' => $model,
      ));
    }
    

    — Добавлены еще несколько статических классов-хелперов: ArrayHelper, StringHelper, SecurityHelper. Все хелперы теперь можно перекрыть через LSB. Ура, воскликнул я, т.к лично мне не раз нужно было перекрыть Html.

    — Виджет ActiveForm тоже переписан, и скорее всего заменит форм-билдер CForm. Каждое поле формы теперь может быть представлено как объект ActiveField, который создает ActiveForm
    $form = $this->beginWidget('yii\widgets\ActiveForm', array(
      'options' => array('class' => 'form-horizontal')
    ));
    
      echo $form->field($model, 'username')->textInput(); 
      echo $form->field($model, 'password')->passwordInput(); 
      echo $form->field($model, 'rememberMe')->checkbox();
    
      echo Html::tag('div', Html::submitButton('Login', null, null, array('class' => 'btn btn-primary')), array(
        'class' => 'form-actions'
       ));
    
    $this->endWidget();
    
    * Внимание: в Html::tag($tag, $content, $options) — изменили порядок параметров!

    ActiveRecord

    «По большей части, ActiveRecord осталась нетронутой»
    — написано в предыдущей статье. Верно подмечено — не трогали.
    Просто взяли и написали совсем другой ActiveRecord.

    — Забываем про model()

    — Убран CDbCriteria. Но не пугайтесь, работа с базой стала от этого легче. Появился ActiveQuery, который представляет себя гибрид CActiveFinder и CDbCriteria.
    // получаем экземпляр ActiveQuery
    $query = Post::find();
    
    // все посты
    $posts = $query->all();
    
    // ищем все посты с условием
    $posts = $query
        ->where(array('status' => Post::DRAFT))
        ->orderBy('time')
        ->all();
    
    // ищем один пост 
    $post = $query
       ->where(array('id' => 10, 'status' => Post::READ))
       ->one();
    
    // или проще, условие where можно передавать прямо в фабричный метод
    $post = Post::find(array('id' => 10, 'status' => Post::READ));
    
    // передав в фабричный метод не массив а число эквивалентно поиску по первичному ключу 
    $post = Post::find(10)
       ->where(array('status' => Post::READ))
       ->one();
    
    // индексируем результат по нужному атрибуту
    $posts = $query->indexBy('title')->all();
    
    // результат в виде массива
    $posts = $query->asArray()->all();
    

    — Все общие методы теперь статические: getDb, tableName, find*, saveAll*, primaryKey. Выигрыш очевиден.

    — Связи, куда же без них. Теперь связи определяются добавлением геттеров
    class Post extends ActiveRecord
    {
        public function getCreator()
        {
            return $this->hasOne('User', array('id' => 'user_id'));
        }
        public function getComments()
        {
            return $this->hasMany('Comment', array('post_id' => 'id'));
        }
        public function getTrustComments($isTrust = true)
        {
            return $this->hasMany('Comment', array('post_id' => 'id'))
                ->where('status = :status', array(
                         ':status' => $isTrust ? self::TRUST : self::UNTRUST,
                  ))
                ->orderBy('id');
        }
    }
    

    — Для удобства работы со связями добавили link() и unlink(), который автоматически расставят ключи
    $post = Post::find(1);
    
    $comment = new Comment();
    $comment->text = 'Yii Framework is cool!';
    $post->link('comments', $comment);
    

    — Именованные группы условий есть, но в другом виде. У нас же нет больше CDbCriteria, а значит и массивов условий тоже больше нет. Теперь это методы, причем статические, добавляющие условия в Query
    class Post extends \yii\db\ActiveRecord
    {
        /**
         * @param ActiveQuery $query
         */
        public static function byCreator($query, $userId)
        {
            $query->andWhere('user_id = :userId', array('userId' => $userId));
        }
        /**
         * @param ActiveQuery $query
         */
        public static function removed($query)
        {
            $query->andWhere('removed = 1');
        }
    }
    
    $posts = Post::find()->removed()->all();
    $myPosts = Post::find()->byCreator(Yii::$app->user->id)->all();
    


    Все

    На этом поставлю точку. Статья получилась большая, много кода, но надеюсь вы выдержали и дочитали.

    До связи.

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 54

      +7
      Отличная статья, такие обзоры-апи и надо делать, меньше воды и больше нормального кода ;) Определение связей отлично сделано, вообще AR очень хорошей получилась. Кстати новая ActiveForm тоже отличная) Yii2 это rails on php :D
      P.S. slavcopost это slavcodev с гитхаба?)
        +3
        Да это я.
          +3
          rails on php это точно. это сейчас две мои любимые технологи: рельсы и yii. ребята, все на yii!))
            0
            Если не затруднит, можно пару слов о yii? На основном рускоязычном сайте не написано, в чем же все прелести yii. На Вики и то больше узнал.
            Можно от знатаков узнать, что больше всего нравится? Или даже не так… почему он так нравится?
              +1
              Я знатоком Yii себя не считаю) Yii он целиком и полностью не такой как другие PHP фреймворки. Те кто юзают там Zend или Symfony обычно смотрят на Yii вот такими вот глазами O_o Мне нравится там все: и архитектура, и виртуальные пути ФС, и ORM (ActiveRecord), и очень чистая реализация MVC, то как там виджеты создаются, как легко подключаются всякие MemCache/CacheLite и т.д. Тут можно долго говорить, надо просто попробовать!)
                +1
                и про gii/giix не забывайте! автоматически создаваемые relations по foreign keys — это просто бальзам на душу
          +3
          Чем-то мне этот ActiveRecord напоминает Kohana ORM
            –2
            А мне почему то нет:
            Yii
            $query->andWhere('user_id = :userId', array('userId' => $userId));
            

            Kohana
            $model->where('user_id', '=', $userId);
            

            у коханы приятнее и логичнее с составлением запросов на мой взгляд
            +1
            Большое спасибо за обзор из первых рук! Столько изменений, что я его уже хочу. Чего стоят одни только переделки связей — коротко и по делу. Кода, правда, больше, но зато он понятный. Кстати, всегда интересовал вопрос — статические методы действительно настолько быстрее методов объектов, чтобы уходить от последних? Есть ли где-нибудь сравнение по ресурсам/скорости 1.13 и 2.0?
            +8
            Всегда было интересно: кто минусует такие топики?
            Ведь и тема свежая, и много примеров с кодом
              +7
              Дизайнеры, к примеру, которые ждут иконок…
                0
                Так есть специально заточенный хаб, как-то глупо ждать иконок в посте о PHP фреймворке. А если мозолит глаза — всегда можно воспользоваться настройками ленты
                  +4
                  блин, ну тогда по ошибке мышка прыгнула.)
                +2
                Очевидно хейтеры Yii.
                +2
                Магия магией погоняет.
                Было засилье инстанцовых методов бизнес логики.
                Стало процедурно-статическое раздолье в моделях. Создал статические метод — вызываешь его инстацово, да еще и без поддержки ИДЕ.

                Почему разработчики так хотят отделить мух от котлет, но, как мне кажется, у них это получилось плохо?! Почему бы просто не разделить букву М в MVC на две составляющие — данные и сервисные методы: Post и PostQuery. В последний поместить все угодные методы byCreator, removed, piblished и тд.

                А в целом команда Yii молодцы :) очень много хороших, на мой взгляд, решений и улучшений.
                  0
                  Кстати такая картина в symfony, в первой части там была модель Post и сразу PostPeer.
                  А во второй symfony репозитории для каждой модели можно создать сразу.
                    0
                    Это в propel. Там и сейчас так
                    +1
                    Почему бы просто не разделить букву М в MVC на две составляющие — данные и сервисные методы: Post и PostQuery

                    И сейчас не сложно их разделить, и будет автокомплит в ИДЕ работать ( подменить метод ActiveRecord::createQuery создать дополнительный класс, подменить возвращаемый тип у find и findBySql для ИДЕ ):
                    ...
                    /**
                     * @method static \app\models\PostActiveQuery find( $q = null )
                     * @method static \app\models\PostActiveQuery findBySql( $sql, $params = array() )
                     */
                    class Post extends \yii\db\ActiveRecord {
                    ...
                    	/**
                    	 * @return PostActiveQuery
                    	 */
                    	public static function createQuery() 	{
                    		return new PostActiveQuery( array(
                    			'modelClass' => get_called_class(),
                    		) );
                    	}
                    }
                    
                    class PostActiveQuery extends ActiveQuery {
                    	/**
                    	 * @param $title
                    	 * @return PostActiveQuery
                    	 */
                    	public function byTitle( $title ) {
                    		$this->andWhere( 'title = :title', array( 'title' => $title ) );
                    		return $this;
                    	}
                    }
                    
                    +2
                    Можно поинтересоваться, почему выбрано так именовать во фреймворке неймспейсы? \yii\db\ActiveRecord вместо Yii\Db\ActiveRecord?
                      –5
                      Нравится и при этом всё по PSR-0. Почему нет?
                        +7
                        Это как-то выделяется от прочих: Doctrine, Zend, Symfony, Monolog, Composer, и проч. Если бы использовали CamelCase это смотрелось бы более органично.
                      +1
                      А можете подсказать в новом ActiveRecord можно делать запросы вида:
                      SELECT ..... FROM (SELECT .... FROM tbl2).....
                      

                      В текущем виде AR + CDbCriteria выполнить такой запрос у меня так и не получилось.

                      P.S. Если вопрос глупый, то прошу прощения, просто начал использовать Yii не так давно и всех тонкостей ещё не знаю.
                        +4
                        $posts = Post::findBySql('SELECT ..... FROM (SELECT .... FROM tbl2)')->all();
                        
                          0
                          А есть для PHP ORM уровня SQLAlchemy? Что бы в таких случаях не надо было опускаться до SQL? (а то СУБД бывают разные и хотелось бы иметь гарантированную прослойку)
                            0
                            Если вы делаете не инсталлируемый продукт, то проблема надумана. А так, чувствую, можно проблему решить без подзапроса. С SQLAlchemy детально не знаком, уровень оценить не могу.
                        +2
                        Очень много действительно полезных изменений, спасибо авторам за проделанный труд!
                        Интересует прогноз команды разработчиков, сколько примерно времени понадобится до стабильной версии yii2?
                          0
                          Думаю, до конца года точно ещё будем делать.
                          0
                          Что с поддержкой бутстрапа? Вижу в форме его используют. Какой функционал еще предоставлен? Спасибо.
                            0
                            Пока больше никакого. А что ещё нужно?
                              +1
                              Ну полной поддержки не нужно. Мне не нравится как сделано в yii-booster где есть виджет даже на Hero unit. Но в своих проектах часто использую виджет на менюшки (удобна поддержка разделителя и многоуровневость, ну и классы соответствующие). Также удобны табы, бредкрампс, пагинация. Было бы удобно если бы был аналог CGridView.
                            0
                            Благодарю за пост, очень понятно и полезно.

                            Говорят в продакшн лучше пока не использовать, а есть ли какие сроки, когда можно будет более или менее начать его использовать?
                            Сам пока плохо знаком с YII, но хочу попробовать на нем написать что нибудь. Хотелось бы конечно начать с YII2, поэтому интересуют примерные сроки стабильных версий.
                            Как я понял с первой версией YII2 несовместим, получается кучу отличных расширений надо будет подождать пока перепишут под вторую версию?
                              0
                              Правильно говорят. Это ещё даже не альфа. К концу года, думаю. Хотя, это совсем-совсем примерно.

                              С расширениями к релизу всё будет нормально.
                              +2
                              Заголовок «Yii2. Знакомство (для знакомых с предметом)» лучше бы подошел. А то читает новичок, радуется, что нашел статью, где узнает с самого начала о предмете, заходит — а там его страшными словами в дрожь вгоняют, притом со знанием дела!
                                0
                                Вот увидел код:
                                $query->andWhere('user_id = :userId', array('userId' => $userId));
                                

                                Ранее, как я помню, надо было вводить
                                $query->andWhere('user_id = :userId', array(':userId' => $userId));
                                

                                Я ошибаюсь или это опечатка?
                                  0
                                  Ранее тоже можно было без двоеточия в AR.
                                    0
                                    С PDO тоже самое.
                                    –2
                                    Рискну кармой, но всё же поинтересуюсь.
                                    Ну вот делаем мы, к примеру, некий веб-проект. Дизайн нарисовали и сверстали. Бизнес-логику, если там вообще есть бизнес-логика, написали — хоть на РНР, хоть на С++, хоть на хранимых процедурах SQL — не суть.
                                    Осталось прикрутить веб-морду по сверстанному дизайну.
                                    И вот тут я вижу два варианта.
                                    Если наш проект — это публичный сайт, то мы берем битрикс (джумлу, друпал, что угодно), специально обученный по выбранной CMS админ расставляет галочки в админке и вписывает названия полей в верстку -> через пару дней проект готов.
                                    Если у нас веб-приложение, то класс View на сервере уже входит в стандартную поставку PHP и называется json_encode. Нужно только добавить орм по вкусу и приправить тоником.
                                    А вот кому сейчас может потребоваться Yii (теперь с классом View!)...? Мне действительно интересно.
                                      +3
                                      Тому кому и Pylons,Ruby on Rails, Django требуется… «специально обученный по выбранной CMS админ » это если ваш публичный сайт полностью укладывается в концепцию CMS.
                                      У вас идёт упор на СУБД, а вот есть проекты где основные разработки идут в области шаблонов и фронтенда и тут гораздо удобнее такого рода фреймворки.
                                        0
                                        Тривиальный backend можно в Yii сделать за один рабочий день, так зачем мне CMS на незначительную кастомизацию которой могут уйти недели? Проблема только в том что порог вхождения выше в Yii, и прогера на поддержку сложнее наверное найти если что. Хотя для простеньких контентых сайтов действительно CMS юзать сподручнее
                                        0
                                        Не являюсь поклонником Yii, потому не пинайте :) Какой смысл в методе populate()? Почему контроллер должен каким-то образом заполнять модель? Намного логичнее выглядит $model->populate($_POST).
                                          0
                                          Раньше (в Yii первом) так и было: $model->setAttributes($_POST['Model']). Тоже не очень понятно, в чём смысл переноса этого метода в контроллер.
                                            0
                                            Не берусь отвечать за авторов, с $model->setAttributes($_POST['Model']) была как минимум одна проблема, там в цикле весь массив $_POST['Model'] обходился, в итоге можно было получить кучу проблем.

                                            Ну и мне кажется что все таки данные от пользователся лучше в контроллере обработать а не в модели.
                                              0
                                              Вот как раз-таки модель и должна определять, какие поля можно установить из этого массива, а какие отбросить.
                                                0
                                                Так и было, она это определяла с помощью предоставления списка валидаторов на поля, а уже куда пихнуть цикл на обход $_POST полей по заданным валидаторам это уже дело вкуса.
                                          +2
                                          1) А связи охранятся автоматичесик без link?
                                          $post = Post::find(1);
                                          
                                          $comment = new Comment();
                                          $comment->text = 'Yii Framework is cool!';
                                          $post->setComments(array($comment));
                                          $post->save();
                                          

                                          Сеттер для связей тоже нужно писать самому? Или всегда вызывать link?
                                          Не шибко удобно получается.

                                          2) Dirty attributes есть?

                                          3)
                                          $form = $this->beginWidget('yii\widgets\ActiveForm', array(
                                            'options' => array('class' => 'form-horizontal')
                                          ));
                                          
                                            echo $form->field($model, 'username')->textInput(); 
                                            echo $form->field($model, 'password')->passwordInput(); 
                                            echo $form->field($model, 'rememberMe')->checkbox();
                                          
                                          

                                          тут можно передать модель сразу в форму? Чтобы не писать $model в каждом тэге.

                                          4) В чем смысл populate, если safe attributes хранятся в модели?

                                          5) Можно ли теперь делать так:
                                          $post->getComments()->approved()->all();// approved - scope класса Comment
                                          


                                          6) Зачем 'echo render;' И вообще явный рендер?
                                          Проще же делать как в зенде, рельсах и т.д. Рендеринг одноименного вью автоматически, а если нужно рендерить что-то другое, то уже вызываем явно.
                                          $this->view->var = 'value'; //zend
                                          $this->var = 'value'; // a-la rails. Переменная var доступна во вью.
                                          


                                          7) Yii::$app->response->redirect(array('site/index')); — зачем использовать статику? У контроллера же может быть поле request?
                                          $this->request->redirect(...);
                                          

                                          Думаю и для тестов так лучше.
                                            0
                                            1) Пока нет. creocoder работает над этим.
                                            2) Да.
                                            3) Пока нет. Синтаксис для виджетов ещё будет меняться.
                                            4) Это черновой вариант. Он нам пока не нравится.
                                            5) Да. Если нет — это баг.
                                            6) Неявность в этом случае совсем не нравится. Это одна строчка в контроллере, из который сразу можно понять, какой view рендерится и какие переменные в нём доступны.
                                            7) Может. Подумаем.
                                            0
                                            Спасибо за ответ. Насчет populate. В рельсах сейчас практикуется такой гем -https://github.com/rails/strong_parameters. Может заинтересует сам подход? Вообще, хорошая идея вынести safe attributes из модели.
                                            Не в ту ветку отправил… это для samdark
                                              0
                                              У нас нет такой проблемы изначально, чтобы её решать. По умолчанию массовое присваивание не работает. Нужно как раз сделать whitelist атрибутов в scenarios и rules.
                                                0
                                                Проблема есть, то что в рельсах называется attr_acсessible, в yii — rules. Идея в том, чтобы вынести эти rules из модели. Модель не должна знать о сценариях ее использования и белом списке. Тогда populate пригодится. Он отсеит то, что мы запретим, в контроллере. При использовании strong_parameters нам просто запрещают присваивать что-либо иное, кроме отфильтрованных данных. Модель становится чище, а безопасность не теряется.
                                                  0
                                                  Тут, по-моему, дело вкуса. По мне так приятней определить N сценариев использования в самой модели, а в контроллере указывать только имя сценария.
                                                    0
                                                    Мы используем giix для создания моделей, а именно двух классов. Первый из них — базовая «чистая» модель, второй — добавление или переопределение правил, поведений, методов для «жирных» моделей. Удобно и отделяются мухи от котлет.
                                              0
                                              Спасибо за статью!

                                              Такой вопрос: Будет ли в Yii2 RESTful Server из коробки?
                                                0
                                                Планируется.

                                              Only users with full accounts can post comments. Log in, please.