В данной статье я расскажу как создать расширение для проекта на фреймворке yii-2, которое будет устанавливаться с помощью Composer. Его можно будет использовать в других своих проектах, а так же опубликовать для использования другими людьми.

В качестве примера, создам тестовое расширение (пакет), в него включу достаточно функционала, чтобы продемонстрировать основные моменты которые могут понадобиться в создании расширений для yii-2. Так, расширение у нас будет в виде модуля со своим контроллером, моделью и видом. Оно будет взаимодействовать с базой данных, поэтому используем миграции. Создадим класс автозагрузки, для установки основных параметров расширения в процессе начальной загрузки приложения, таким образом мы зарегистрируем сам модуль непосредственно в этом классе, а так же зададим правила маршрутизации там же, что снимет с устанавливаемого данное расширение человека дополнительные его настройки. Фактически пользователю понадобится выполнить всего 2 команды в консоли:
  • установить расширение;
  • выполнить миграцию.
и расширение станет полностью работоспособным.
Так же покажу как автоматически подключить assets файлы расширения – CSS или JS.
В качестве результата своей работы, расширение будет выводить данные из БД на отдельной странице.

Назовем расширение «mytest» и его структура будет такова:



При разработке своего расширения с нуля, в yii2 можно пользоваться помощником для создания начальных файлов, таких как composer.json, README.md.

Перейти в помощник можно набрав в адресной строке:
http://NAME/gii/extension
а если не включено ЧПУ URL, то:
http://NAME/web/index.php?r=gii/default/view&id=extension


В данном помощнике нужно заполнить несколько обязательных полей согласно изображения. Прежде всего Вам нужно иметь аккаунт на GitHub, название которого необходимо прописать в поле Vendor Name (у меня klisl). Это будет корневой каталог в котором будут создаваться папки каждого из ваших расширений, название которых прописывается в поле Package Name. Стоит отметить, что разработчики данного фреймворка рекомендуют использовать префикс «yii2-» в названии расширений (пакетов), являющихся расширениями Yii-2. Поэтому я указал yii2-mytest.

В поле Namespace нужно указать пространство имен вашего расширения чтобы автозагрузчик Composer смог отыскать файлы и чтобы название классов расширения не конфликтовали с одноименными классами других расширений и самого фреймворка. Лучше всего пространство имен указывать как название вашего аккаунта на GitHub\название расширения (пакета) без приставки «yii2-».
В качестве типа указывается расширение или же библиотека (содержащая классы), а в качестве лицензии обычно выбирается MIT, что значит бесплатно распространяется и используется кем угодно без ограничений на свой страх и риск.

Отправив заполненную форму помощника, по-умолчанию, у вас создастся 3 файла в папке runtime\tmp-extensions. Это файлы, которые изначально находятся в vendor\yiisoft\yii2-gii\generators\extension\default и потом копируются туда с подставленными значениями из формы.

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


Помощник создания расширений в yii2 предполагает сразу после создания начальных файлов в временной папке runtime\tmp-extensions синхронизацию их с удаленным репозиторием типа GitHub и дальнейшей работой вплотную с помощью git. Я же расширения для фреймворков создаю как отдельные приложения. Например используя Опенсервер, в данном случае, я бы создал каталог klisl на уровне других приложений и в нем уже папки расширений:
W:\domains\klisl\yii2-mytest
Таким образом работаю над расширением локально и выгружаю на GitHub уже более-менее сформированное расширение с одновременной регистрацией его на Packagist (если планируется сделать свое расширение доступным для общественности) для окончательного тестирования.

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


Создание файлов расширения.

Создадим теперь файл composer.json нашего расширения.
Опять же, можно создать данный файл в интерактивном режиме используя консоль и команду
composer init
Перед этим нужно перейти в корневой каталог расширения.
Если Composer установлен не глобально, то вместо
composer
нужно писать
php composer.phar
Данные тонкости не относятся к теме этой статьи.
Будет предложено ввести данные по названию проекта, автору, предложено сразу вписать нужные зависимости.

Все файлы composer.json имеют одни и те же поля и структуру, поэтому опять же, можно просто взять за основу другой подобный файл. Для нашего тестового расширения он будет выглядеть так:
{
    "name": "klisl/yii2-mytest",
    "description": "Package",
    "type": "yii2-extension",
    "keywords": [
        "yii2",
        "test"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Ivan Ivanov",
            "email": "ivan@ukr.net",            
            "homepage": "https://github.com/klisl/yii2-mytest",
            "role": "Developer"
        }
    ],
     "minimum-stability": "stable",
     "require": {
        "yiisoft/yii2": "~2.0.0"
    },
    "extra": {
        "bootstrap": "klisl\\mytest\\Bootstrap"
    },
    "autoload": {
        "psr-4": {
            "klisl\\mytest\\": "src"
        }
    }
}
Немного подробностей.
В основном файле расширения «composer.json» нужно указать тип пакета:
    "type": "yii2-extension",
чтобы пакет можно было распознать как расширение Yii во время установки.
Когда пользователь запустит команду для установки расширения, файл vendor/yiisoft/extensions.php будет автоматически обновлён, чтобы включить информацию о новом расширении. Из этого файла приложение Yii может узнать, какие расширения установлены (информацию о установленных расширениях можно получить с помощью вызова yii\base\Application::extensions).

В объекте "require" указываются зависимости вашего пакета. Например стоит указать:
    "require": {
        "yiisoft/yii2": "~2.0.0"
    },
т.к. если расширение для фреймворка yii2, то и работать без него оно не может.

Важным моментом для многих расширений является возможность выполнить определенный код в процессе начальной загрузки приложения, еще до выполнения кода контроллера и прочего. Например если понадобится внедрить свои правила маршрутизации, контроллеры… В Yii2 для этого используется так называемый класс начальной загрузки. Класс начальной загрузки должен реализовывать интерфейс yii\base\BootstrapInterface. Пример класса автозагрузки я создам в нашем тестовом расширении, чтобы продемонстрировать данную возможность. На данном этапе нужно только его подключить в composer.json расширения, что сделано в поле
"extra": {
    "bootstrap": "klisl\\mytest\\Bootstrap"
},

А в поле
    "autoload": {
        "psr-4": {
            "klisl\\mytest\\": "src"
        }
    }
я указал, что для автозагрузки классов расширения с пространством имен klisl\mytest нужно искать их в каталоге src. При этом применяется стандарт кодирования PSR-4, согласно которому используется пространство имен классов и файлы классов имеют названия соответствующие названиям находящихся в них классов. Подробнее https://zenwalker.me/blog/php-psr-0-vs-psr-4

С основным файлом расширения закончили. Так же, если вы планируете сделать ваше расширение общедоступным, следует создать файл README.md описывающий как устанавливать и использовать ваш пакет. Данный файл можно сформировать автоматически непосредственно при создании нового репозитория для вашего расширения на GitHub и потом отредактировать или же скопировать код другого подобного файла. Пример можно посмотреть в архиве с файлами тестового расширения.

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

Для демонстрации подключения стилей или скриптов создадим такой файлик:
Файл assets/css/style.css
.block{
    max-width: 1170px;
    margin: 0 auto;
}
.red {
    color: red;
}

Далее, двигаясь сверху вниз по изображению структуры нашего расширения создаем его контроллер:
Файл src/controllers/TestController.php
<?php
namespace klisl\mytest\controllers;
use Yii;
use yii\web\Controller;
use klisl\mytest\models\Tests;
class TestController extends Controller
{
    public $layout = 'main';
    public function actionIndex()
    {
        // регистрируем ресурсы:
        \klisl\mytest\TestsAssetsBundle::register($this->view);
        $datas = Tests::find()->all();
        return $this->render('index',[
            'datas' => $datas
        ]);
    }
}

Для всех классов в каталоге src не забываем указывать обозначенное в файле composer.json расширения пространство имен klisl\mytest и далее в зависимости от папки в которой находится текущий класс. Так автозагрузчик Composera сможет его отыскать согласно стандарта psr-4.
Контроллер создается и используется стандартно. В нем я регистрирую класс TestsAssetsBundle для подключения нашего созданного выше файла стилей. Данный класс создадим чуть ниже.
Далее получаем данные из БД и передаем в вид.

Для демонстрации создания таблицы в базе данных для нужд расширения создаем файл миграции. Его можно создать обычным способом в основном приложении:
yii migrate/create tests
и далее перенести в папку src/migrations расширения или создать вручную.

Класс миграции не должен содержать пространство имен.
Название таблиц стоит заключать в фигурные скобки со знаком процента спереди для того, чтобы к названию автоматически подставлялся префикс таблиц используемый пользователем (если установлен префикс в настройках данного пользователя). Префиксы указываются в файле:
  • для basic (config\db.php);
  • для advanced (common\config\main-local.php) в массиве db.
Пример создания таблицы с названием «counts» с подстановкой префикса:
$this->createTable('{{%counts}}', [
   'id' => $this->primaryKey(),
   ...
], $tableOptions);

Файл src/migrations/m170306_083010_tests.php
<?php
use yii\db\Migration;
class m170306_083010_tests extends Migration
{
    public function safeUp()
    {
      $tableOptions = null;
      //Опции для mysql
        if ($this->db->driverName === 'mysql') {
            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
        }
      //Создание таблицы IP пользователей
      $this->createTable('{{%tests}}', [
         'id' => $this->primaryKey(),
         'ip' => $this->string(15)->notNull(),
         'comment' => $this->string(255),
      ], $tableOptions);
    }
    public function safeDown()
    {
         $this->dropTable('{{%tests}}');
    }
}

Раз есть миграция значит должна быть модель для работы с данной таблицей.
Файл src/models/Tests.php
<?php
namespace klisl\mytest\models;
use Yii;
use yii\db\ActiveRecord;
class Tests extends ActiveRecord{
    public function rules()
    {
        return [
            [['ip'], 'ip'],
            [['comment'], 'required'],
        ];
    }
}

Теперь файл-представление (вид).
Файл src/views/test/index.php
<?php if (isset($datas)): ?>
<div class="block">
    <h3 class="red">Вывод тестовых данных</h3>
    <?php foreach ($datas as $data ): ?>
        <p><?=$data->ip?></p>
        <p><?=$data->comment?></p>
    <?php endforeach; ?>
</div>
<?php endif; ?>

Структуру MVC мы создали, осталось еще несколько файлов управляющих модулем.

Создаем файл выполняющийся при загрузке фреймворка (класс начальной загрузки)
Файл src/Bootstrap.php:
<?php
namespace klisl\mytest;
use Yii;
use yii\base\BootstrapInterface;
class Bootstrap implements BootstrapInterface{
    //Метод, который вызывается автоматически при каждом запросе
    public function bootstrap($app)
    {
        //Правила маршрутизации
        $app->getUrlManager()->addRules([
            'test' => 'mytest/test/index',
        ], false);
        /*
         * Регистрация модуля в приложении
         * (вместо указания в файле frontend/config/main.php
         */
         $app->setModule('mytest', 'klisl\mytest\Module');
    }
}

Напоминаю, что данный класс должен реализовывать интерфейс BootstrapInterface и его метод bootstrap для того, чтобы он выполнился при начальной загрузке приложения. Это нам нужно для регистрации своего правила маршрутизации - сделаем, чтобы ссылка на страницу расширения открывалась по ссылке
http://NAME/test
без указания названия модуля/контроллера/вида.
Так же тут мы регистрируем сам модуль, чтобы не просить пользователя его установившего с помощью Composer проделывать это вручную в файле frontend/config/main.php

Далее создаем класс модуля, т.к. наше расширение будет работать как отдельный модуль в yii-2.
Файл src/Module.php
<?php
namespace klisl\mytest;
use yii\base\Module as BaseModule;
class Module extends BaseModule
{
    public $controllerNamespace = 'klisl\mytest\controllers';
}

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

Ну и завершающим файлом создадим файл с классом подключающим наш CSS файл к файлу-представления. Напомню, что данный класс мы зарегистрировали для представления нашего расширения в его контроллере:
\klisl\mytest\TestsAssetsBundle::register($this->view);

Файл src/TestsAssetsBundle.php
<?php
namespace klisl\mytest;
use yii\web\AssetBundle;
class TestsAssetsBundle extends AssetBundle
{
    public $sourcePath = '@vendor/klisl/yii2-mytest/assets';
    public $css = [
        'css/style.css'
    ];
}

Тут мы указываем путь к папке со стилями и скриптами в свойстве $sourcePath и сами файлы в свойстве
$css и $js


Подключение локального расширения.
Т.к. расширение находится на локальном компьютере, необходимо указать Composerу находящемуся в корне основного проекта в котором будем его тестировать от куда нужно скачивать расширение. В нашем случае просто создастся ссылка на папку с ним.
Для этого в файле composer.json yii-проекта добавим секцию:
… ,
 "repositories": [
        {
            "type": "path",
            "url": "../klisl/yii2-mytest"
        }
]

По значению поля «url» видно, что необходимо подняться на один уровень выше, то есть выйти в общий каталог проектов и далее подключить файл composer.json уже самого расширения.

Кроме этого нужно подключить его, т.е. обновить зависимости файла composer.json основного проекта, а так же чтобы обновился автозагрузчик классов у Composer. Для этого нужно выполнить команду из корня основного проекта:
composer require klisl/yii2-mytest:dev-master --prefer-source

После этого добавится новая строка с нашим расширением в файл composer.json в секции require:
"klisl/yii2-mytest": "dev-master"
а так же ярлык на каталог расширения в папке vendor\klisl.

Расширение автоматически получит алиас, в нашем случае
@klisl/mytest
который будет вести на
…\vendor/klisl/yii2-mytest/src
т.к. в файле composer.json самого расширения есть такой блок:
"autoload": {
  "psr-4": {
    "klisl\\mytest\\": "src"
  }
}
Вы можете получить данный путь и вывести его на экран таким образом:
echo Yii::getAlias('@klisl/mytest');

Теперь наше расширение можно найти в файле vendor/yiisoft/extensions.php где очень наглядно видно куда ведет созданный алиас, а так же наличие массива bootstrap, в значении у которого находится путь к файлу автозагрузки расширения.


Для создания таблицы нужно перейти в корень основного проекта и выполнить миграцию:
yii migrate --migrationPath=@klisl/mytest/migrations --interactive=0
тут мы указываем используя алиас @klisl/mytest путь к каталогу src расширения и далее в папку migrations к файлу миграции. Ключ --interactive=0 значит, что при выполнении данной команды не нужно спрашивать подтверждение о создании таблицы пользователя. Т.к. раз расширению она нужна, то создавать нужно в любом случае.


Все готово для запуска расширения!
Добавите пару строк в базу данных для тестирования.
Для открытия его страницы расширения нужно набрать в адресной строке
  • если включено ЧПУ URL:
http://NAME/test
  • если не включено:
http://NAME/web/index.php?r=mytest/test/index

Если вы увидели надписи значит все работает.


Перенос на удаленный репозиторий.
Далее можно инициализировать git в корне расширения и отправить его на удаленный репозиторий GitHub, в котором, предварительно, уже должен быть создан репозиторий с названием «yii2-mytest». Примерный набор команд для этого:
git init
git add .
git commit -m "First commit"
git remote add origin https://github.com/klisl/yii2-mytest.git
git push -u origin master
git tag -a 1.0 -m "New version"
git push --tags
где после строки git remote add origin нужно подставить адрес вашего репозитория. Как видите, он повторяет название корневой папки (поставщика) и папки с пакетом. Думаю работать с git вы уже умеете, раз учитесь создавать свои расширения. Так же, тут указываем версию пакета, что понадобится, если вы собираетесь опубликовать его в репозитории публичных расширений на сайте https://packagist.org

Сервис packagist.org используется Composer для поиска пакетов. Если планируете показать свой пакет общественности то после регистрации нужно будет указать URL к вашему пакету на GitHub по нажатию кнопки «Submit». Это если вкратце.

В файле README.md необходимо будет указать как ваш пакет устанавливается. Для этого нужно будет включить кроме описание самого приложения и примеров работы с ним такие строки:
Установка
------------------
* Установка пакета с помощью Composer.
```
composer require klisl/yii2-mytest
```
* Выполнить миграцию для создания нужной таблицы в базе данных (консоль):
```
yii migrate --migrationPath=@klisl/mytest/migrations --interactive=0
```

Команда
composer require klisl/yii2-mytest
обновит зависимости файла composer.json проекта, скачает само расширение и обновит автозагрузчик.


Не забудьте протестировать работу вашего расширения на этот раз уже при установке из удаленного репозитория согласно вашей же инструкции из файла README.md
Если тестировать будете в том же проекте что тестировали локальную версию расширения, не забудьте удалить из файла composer.json проекта секцию repositories и строку с зависимостью "klisl/yii2-mytest": "dev-master" (и запятую перед этой строкой). А так же таблицу из базы данных.
После этого выполните команду:
composer update
чтобы удалить локальное расширение из проекта.

Ссылка на архив с файлами тестового расширения тут.