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();
    


    Все

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

    До связи.
    Метки:
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 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 репозитории для каждой модели можно создать сразу.
                • +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 из коробки?

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