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

Digital-студия WNDER

Что такое компонент и расширение в Yii2

Для начала давайте разберемся с терминами. В Yii2 есть два близких понятия: компонент и расширение.

Компонент — это класс, который можно подключить к приложению через конфигурацию. Он может быть как простым (например, работа с почтой), так и сложным (например, полноценный модуль). Компоненты обычно регистрируются в секции components конфигурационного файла.

Расширение — это пакет, который распространяется через Composer. Он может содержать компоненты, модули, виджеты, поведения, консольные команды и даже целые приложения. Расширение — это способ упаковать ваш код для многократного использования, как в своих проектах, так и для сообщества.

Проще говоря, компонент — это кирпичик, а расширение — это коробка с кирпичиками и инструкцией по сборке.

Первый шаг: создаем компонент

Начнем с простого — создадим компонент для работы с API какого-нибудь сервиса. Пусть это будет сервис отправки SMS. Наш компонент будет иметь метод send($phone, $message).

Создаем файл components/SmsSender.php:

namespace app\components;

use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;

class SmsSender extends Component
{
    public $apiKey;
    public $apiUrl = 'https://sms.ru/api';

    public function init()
    {
        parent::init();
        if (empty($this->apiKey)) {
            throw new InvalidConfigException('Необходимо указать apiKey');
        }
    }

    public function send($phone, $message)
    {
        // Здесь логика отправки SMS через HTTP-запрос
        $response = file_get_contents($this->apiUrl . '?key=' . $this->apiKey . '&phone=' . $phone . '&text=' . urlencode($message));
        return strpos($response, '100') !== false;
    }
}

Теперь подключаем его в конфигурации приложения (config/web.php):

'components' => [
    'sms' => [
        'class' => 'app\components\SmsSender',
        'apiKey' => 'ваш_ключ',
    ],
],

Готово! Теперь в любом месте приложения можно вызвать:

Yii::$app->sms->send('+79991234567', 'Привет!');

Обратите внимание: мы наследуемся от yii\base\Component, чтобы получить такие бонусы, как события и поведения. Если они не нужны, можно наследоваться от yii\base\BaseObject — это немного легче.

Превращаем компонент в расширение

Теперь, когда компонент работает, захотелось использовать его в другом проекте. Самое время упаковать его в расширение. Для этого создаем отдельную директорию, например, sms-sender, и внутри — composer.json.

Минимальный composer.json:

{
    "name": "myname/sms-sender",
    "description": "Компонент для отправки SMS",
    "type": "yii2-extension",
    "require": {
        "yiisoft/yii2": "*"
    },
    "autoload": {
        "psr-4": {
            "myname\\smssender\\": "src/"
        }
    }
}

Переносим наш класс в src/SmsSender.php, меняем неймспейс на myname\smssender.

Теперь публикуем пакет на GitHub, добавляем тег версии (например, v1.0.0), затем регистрируем его на Packagist. После этого любой разработчик может установить его через Composer:

composer require myname/sms-sender

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

Модуль как расширение: когда компонента мало

Иногда функционал настолько объемный, что нужен модуль. Например, модуль для комментариев или блога. Модуль — это тоже класс, но он содержит контроллеры, представления, модели, миграции и прочее.

Создадим модуль blog. Структура:

blog/
    src/
        BlogModule.php
        controllers/
            PostController.php
        models/
            Post.php
        views/
            post/
                index.php
                view.php
        migrations/
            m240101_000000_create_post.php
    composer.json

В BlogModule.php:

namespace myname\blog;

use yii\base\Module;

class BlogModule extends Module
{
    public $controllerNamespace = 'myname\blog\controllers';

    public function init()
    {
        parent::init();
        // Пользовательская инициализация
    }
}

Подключение в приложении:

'modules' => [
    'blog' => [
        'class' => 'myname\blog\BlogModule',
    ],
],

Теперь по адресу /?r=blog/post/index будет доступен список постов.

В модуле можно использовать миграции. Для этого нужно настроить путь к миграциям в composer.json:

"extra": {
    "yii2": {
        "migrations": [
            "myname\blog\migrations"
        ]
    }
}

После установки расширения миграции будут автоматически подхватываться командой yii migrate.

Виджеты и поведения: добавляем «фишек»

Расширение может содержать не только компоненты и модули, но и виджеты. Виджет — это класс, который выводит HTML-код. Например, виджет облака тегов.

namespace myname\blog\widgets;

use yii\base\Widget;
use myname\blog\models\Tag;

class TagCloud extends Widget
{
    public $limit = 10;

    public function run()
    {
        $tags = Tag::find()->orderBy(['frequency' => SORT_DESC])->limit($this->limit)->all();
        return $this->render('tag-cloud', ['tags' => $tags]);
    }
}

А поведения — это способ переиспользовать функциональность между моделями. Например, поведение для автоматической генерации slug (ЧПУ) из названия.

namespace myname\blog\behaviors;

use yii\base\Behavior;
use yii\db\ActiveRecord;
use yii\helpers\Inflector;

class SluggableBehavior extends Behavior
{
    public $sourceAttribute = 'title';
    public $slugAttribute = 'slug';

    public function events()
    {
        return [
            ActiveRecord::EVENT_BEFORE_INSERT => 'makeSlug',
            ActiveRecord::EVENT_BEFORE_UPDATE => 'makeSlug',
        ];
    }

    public function makeSlug($event)
    {
        $model = $this->owner;
        if (empty($model->{$this->slugAttribute})) {
            $model->{$this->slugAttribute} = Inflector::slug($model->{$this->sourceAttribute});
        }
    }
}

Теперь в любой модели можно просто прикрепить это поведение:

public function behaviors()
{
    return [
        [
            'class' => SluggableBehavior::class,
            'sourceAttribute' => 'title',
            'slugAttribute' => 'slug',
        ],
    ];
}

Правила и лайфхаки при создании расширений

Создание расширений — это не только код, но и забота о пользователях. Вот несколько правил, которые я выработал за годы работы.

Правило «Сделай сам»: Прежде чем публиковать расширение, используйте его в реальном проекте хотя бы месяц. Это поможет выявить баги и неудобства, которые вы не заметили при написании кода.
  • Документируйте все публичные свойства и методы — люди не любят читать чужой код, чтобы понять, как работать с вашим расширением.
  • Следуйте стандартам PSR — это обеспечит совместимость с другими библиотеками и инструментами.
  • Используйте семантическое версионирование (SemVer) — это поможет пользователям понять, насколько безопасно обновляться.
  • Пишите тесты — даже простые тесты значительно повышают доверие к вашему коду.
  • Публикуйте исходники на GitHub — это бесплатно и позволяет другим вносить вклад.

Также полезно добавить в расширение консольные команды. Например, для очистки кэша или генерации отчета. Для этого создайте класс, унаследованный от yii\console\Controller, и укажите его в composer.json:

"extra": {
    "yii2": {
        "commands": [
            "myname\blog\commands\PostController"
        ]
    }
}

Что в итоге

Создание расширений и компонентов для Yii2 — это отличный способ структурировать код и делиться им с другими. Начните с малого: превратите часто используемый компонент в расширение, затем добавите модуль, виджеты и поведения. Главное — не забывайте про документацию и тесты. Тогда ваше расширение будет полезным и востребованным.

Если вы хотите, чтобы ваш код стал популярным, опубликуйте его на Packagist и расскажите о нем в сообществе Yii. Удачи в разработке!

Студия WNDER