Общая архитектура Yii2 Климов П.В. YiiSoft Область применения • Построение функциональности не имеющей аналогов • Новая интерпретация уже существующих бизнес процессов • Стык или объединение различных областей и процессов • Системы с повышенными требованиями к качеству Основные характеристики • • • • • PHP 5.4 ООП Модульность Простота Высокое быстродействие Управление зависимостями Composer Manages PHP packages "request non-PHP package" composer-asset-plugin Substitute Bower client, compatible with composer "use Bower protocol as client" Bower Manages CSS and JavaScript packages Автозагрузка классов // Autoload via Composer by PSR-4 : $model = new yii\base\Model(); // Yii built-in autoload by PSR via alias: Yii::setAlias(‘@app’, ‘/var/www/myproject’); $someObj = new app\models\Customer(); // include ‘/var/www/myproject/models/Customer.php’; Магия в PHP class Object { public $publicProperty; private $_privateProperty; public function setPrivateProperty($value) { $this->_privateProperty = $value; } public function getPrivateProperty() { return $this->_privateProperty; } } class Object { public function __get($propertyName) { $methodName = 'get‘ . $propertyName; if (method_exists($this, $methodName)) { return call_user_func([$this, $methodName]); } else { throw new Exception("Missing property {$propertyName}'!"); } } public function __set($propertyName, $value) { $methodName = 'set‘ . $propertyName; if (method_exists($this, $methodName)) { return call_user_func([$this, $methodName], $value); } else { throw new Exception("Missing property {$propertyName}'!"); } } $object = new Object(); $object ->publicProperty = 'Public value'; echo ($object->publicProperty); $object->privateProperty = 'Private value'; echo ($object->privateProperty); Порождение объектов function createObject(array $config) { $className = $config['class']; if (empty($className)) { throw new Exception(‘Missing parameter "class"!'); } unset($config['class']); $object = new $className(); foreach ($config as $name=>$value) { $object->$name = $value; // Конфигурация } return $object; } Задание любого объекта через массив: $config = [ 'class‘ => ‘yii\web\UrlManager', 'enablePrettyUrl‘ => true, 'showScriptName‘ => false, 'rules‘ => [ '/‘ => 'site/index', '<controller:\w+>/<id:\d+>*‘ => '<controller>/view', ], ]; $object = Yii::createObject($config); Dependency Injection (DI) di\ServiceLocator $definitions $components get($id) “Get class instance” “Request component by id” 1 Client “Create and store by id” * base\Component “instantiate” di\Container $definitions $dependencies $singletons set($class, $params) get($class) ComponentA … di\ServiceLocator base\Module base\Application $config = [ 'name‘ => 'My Web Application', … 'components‘ => [ 'user‘ => [ ‘enableAutoLogin‘ => true, ], … ], ]; (new yii\web\Application($config))->run(); … $application = Yii::$app; $user = Yii::$app->get(‘user’); MVC в Yii2 1 1 Application * 1 Module Widget * * Component * “create” View * Model C * Controller “render” 1 “render part” * “register” * * AssetBundle V M Маршрутизация web запроса :Application web server ‘run’ :Request :UrlManager ‘handle’ ‘resolve’ ‘data’ ‘route’ :Controller ‘run action’ ‘response’ ‘compose output’ ‘output’ ‘output’ :Response Построение отображения class View extends Component { public function renderFile($viewFile, $data = null) { require($viewFile); } } <html> <body> <h1>Data <?php echo $data; ?></h1> <hr /> <?php $this->render(‘main_menu.php’); ?> </body> </html> Структура отображения “determine view path” View render() 1 1 ViewContextInterface “by file extension” 1 * ViewRenderer “Define specific renderer” smarty\ViewRenderer handles *.tpl twig\ViewRenderer handles *.twig Виджет (Widget) <?php echo GridView::widget([ 'dataProvider' => $dataProvider, 'options' => ['class' => 'detail-grid-view table-responsive'], 'filterModel' => $searchModel, 'columns' => [ 'time', 'level', 'category', 'message', ], ]); ?> Asset Management Client Shortcut static method "register()" View “Depends on” $assetBundles registerAssetBundle() “Get Asset Bundle” AssetManager $bundles 1 “Create, override and register” * AssetBundle $css $js $depends getBundle() “Compose actual files” AssetConverter convert() JqueryAsset … class YiiAsset extends AssetBundle { public $sourcePath = '@yii/assets'; public $js = [ 'yii.js', ]; public $depends = [ 'yii\web\JqueryAsset', ]; } class JqueryAsset extends AssetBundle { public $sourcePath = '@bower/jquery/dist'; public $js = [ 'jquery.js', ]; } Модель Model “Create and run” setAttributes() getAttributes() validate() 1 * Validator “Define specific validator” EmailValidator RangeValidator … Доступ к базе данных через PDO Client PDO 1 1 PDO Driver PDO MySQL PDO PostgreSQL MySQL PostgreSQL … Абстракция базы данных PDO 1 1 “Syntax” 1 1 QueryBuilder “Use” 1 * mysql\QueryBuilder Connection 1 * “Types, keys, etc.” “Create” Command … 1 1 “Use” * 1 Schema mysql\Schema … Select Query Shortcut 1 Query “Create” * BatchQueryResult “Create and execute” 1 * * “Build” Command 1 QueryBuilder 1 Iterator 1 DataReader Countable Active Record BaseActiveRecord save() delete() Shortcut static method "find()" ActiveRecord 1 * ActiveRelationTrait * 1 “Instantiate by query result” ActiveQuery “Database access” Command Query // Выборка записей: $allUsers = User::find()->all(); // Вставка новой записи: $newUser = new User(); $newUser->name = ‘new user’; $newUser->save(); // Обновление существующей записи: $existingUser = User::find()->where([‘name’=>‘testuser’])->one(); $existingUser->email = ‘newemail@domain.com’; $existingUser->save(); // Отложенная загрузка отношения: $bio = $existingUser->profile->bio; // Жадная загрузка отношения: $allUsers = User::find()->with(‘profile’)->all(); «Жадная» загрузка отношений :ActiveQuery Client ‘find with "profile"’ ‘create from query result’ :User ‘get "profile" info’ ‘relation info’ :ActiveQuery ‘find where userId in set’ ‘profile references’ ‘bind "profile"’ ‘result’ :Profile NOSQL Active Record • • • • MongoDB Redis ElasticSearch Sphinx Cross-DBMS отношения BaseActiveRecord db\ActiveRecord mongodb\ActiveRecord “Has relation” User Profile События (Events) в Yii Component 1 “Raise” eventHandlers Event sender data trigger() 1 * 1 PHP Callback “Handle” * Handler “List of PHP callbacks” * Обработка события Event Handler :ActiveRecord Client ‘save’ ‘raise “beforeInsert”’ :Event * ‘handle “beforeInsert”’ ‘get event info’ ‘sender’ ‘adjust event sender’ ‘saving result’ ‘actual data saving’ function handleBeforeInsert(Event $event) { $sender = $event->sender; // Изменяем состояние отправителя события: $sender->create_date = date('Y-m-d', strtotime('NOW')); } $user = new User(); // Назначаем обработчик события: $user->on(‘beforeInsert’, ‘handleBeforeInsert’); $user->name = ‘test name’; $user->save(); echo $user->create_date; // Вывод: ‘2015-03-21’ Проблема множественного наследования ActiveRecord ArPosition ArFile Save custom records display order Bind physical file with the db record ArPositionFile Position + File Поведение (Behavior) 1 * Component Behavior behaviors owner __call() attachBehavior() getOwner() events() 1 1 “Raise” * Event data * “Handle” Расширение интерфейса class Component { private $_behaviors = [ ]; public function __call($method, $arguments) { // Ищем недостающий метод среди поведений: foreach ($this->_behaviors as $behavior) { if (method_exists($behavior, $method)) { return $behavior->$method($arguments); } } throw new Exception(“Missing method {$method}”); } … } class ArBehaviorExample extends Behavior { public function behaviorMethod() { $this->owner->create_date = date('Y-m-d', strtotime('NOW')); } } $user = new User(); // Добавляем поведение: $behavior = new ArBehaviorExample(); $user->attachBehavior($behavior); // Вызываем метод поведения: $user->behaviorMethod(); echo $user->create_date; // Вывод: ‘2015-03-21’ Обработка событий class ExampleBehavior extends Behavior { public function events() { return [ ‘beforeInsert’ => ‘handleBeforeInsert’, ‘afterInsert’ => ‘handleAfterInsert’ ]; } public function handleBeforeSave(Event $event) { // Обработка события «beforeInsert» } … } Отправка электронной почты yii\mail MailerInterface 1 “Send” * MessageInterface Shortcut method "send()" BaseMailer BaseMessage yii\swiftmailer Mailer Message Расширение «AuthClient» Collection 1 * ClientInterface “Configure and create” BaseClient OAuth1 Twitter BaseOAuth OAuth2 Facebook OpenId Yandex Дополнительные расширения • • • • • • • Gii Debug Boostrap JUI Codeception Imagine ApiDoc Yii2 • • • • • Composer + Bower Компонентная структура и DI MVC Управление «assets» PDO и Active Record • • • • • NOSQL и Active Record Cross-DBMS отношения События Поведения Стандартные расширения ZFort приглашает на работу: Senior Magento Developer / TechLead Middle ASP.NET Developer Senior Project Manager Middle PHP CMS Developer Middle FrontEnd Developer