logo

Блог

Создание собственных скоупов для ActiveRecord в Yii2

В сегодняшней статье я расскажу об создании собственных скоупов для выборки записей из базы данных для модели ActiveRecord в фраемворке yii2. Итак, начнем.

Для примера я возьму basic версию фраемворка. Сначала нам нужно создать два класса ActiveQuery и ActiveRecord, которые будут наследовать базовые классы фраемворка.

Создадим в корне проекта директорию components

и в ней создадим два класса ActiveQuery и ActiveRecord

В классе ActiveQuery будет описан скоуп active который позволяет выбирать записи из базы данных у которых свойство "state" равное переданному в него значению, по-умолчанию это значение равное "1", однако сюда можно передавать и другие значения.

Код класса ActiveQuery (components/ActiveQuery.php)

namespace app\components;

use yii\db\ActiveQuery as YiiActiveQuery;

class ActiveQuery extends YiiActiveQuery
{
    public function active($state = 1)
    {
        return $this->andWhere(['state' => $state]);
    }
}

Класс ActiveRecord должен наследоваться от базового класса ActiveRecord и в нем нужно переопределить метод find, в котором будет создаваться экземпляр вышеописаного класса ActiveQuery.

Код класса ActiveRecord (components/ActiveRecord.php)

namespace app\components;

use yii\db\ActiveRecord as YiiActiveRecord;
use app\components\ActiveQuery;

class ActiveRecord extends YiiActiveRecord
{
    public static function find()
    {
        return new ActiveQuery(get_called_class());
    }
}

После этого мы можем наследовать все базовые классы ActiveRecord от только что созданного класса ActiveRecord и применять в моделях скоуп active. Для примера я создал таблицу книг(book) и заполнил ее записями, чтобы продемонстрировать как все это работает.

После чего с помощью gii генератора создадим модель для нашей таблицы, а в качестве BaseClass укажем наш созданный класс ActiveRecord

Класс Book (app/models/Book.php)

namespace app\models;

use Yii;

/**
 * This is the model class for table "{{%book}}".
 *
 * @property int $id
 * @property string $name
 * @property string $description
 * @property int $active
 */
class Book extends \app\components\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%book}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['id', 'name'], 'required'],
            [['id', 'active'], 'integer'],
            [['description'], 'string'],
            [['name'], 'string', 'max' => 255],
            [['id'], 'unique'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'name' => 'Name',
            'description' => 'Description',
            'active' => 'Active',
        ];
    }
}

Теперь когда наша модель для таблицы book создана, мы можем написать экшен для вывода всех книг с применением нашего скоупа (я создал отдельный контроллер для работы с моделью Book)

Код контроллера (controllers/BooksController.php)

namespace app\controllers;

use app\models\Book;

class BooksController extends \yii\web\Controller
{
    public function actionIndex()
    {
        $books = Book::find()
            ->active()
            ->all();

        return $this->render('index', compact('books'));
    }
}

и код представления для отображения книг (views/books/index.php)

<div class="row">

    <?php foreach ($books as $book) : ?>

        <div class="col-lg-4 col-md-4">
            <div class="panel panel-primary">
                <div class="panel-heading"><?= $book->name; ?></div>
                <div class="panel-body"><?= $book->description; ?></div>
            </div>
        </div>

    <?php endforeach; ?>

</div>

И перейдя по роуту контроллера books экшена index (у меня это /index.php?r=books/index) мы увидим результат проделанной работы

Вот и все, теперь мы можем наследовать любую модель ActiveRecord от только что созданного класса ActiveRecord. Но самое главное, что для того чтобы создать новые скоупы нам всего лишь придется расширить класс app\components\ActiveQuery и новые скоупы будут уже доступны в моделях наследованных от app\components\ActiveRecord.

Также существует еще один способ создания собственных скоупов для ActiveRecord моделей. Он отличается от вышеописанного только тем, что Вам не придется создавать собственный класс ActiveRecord, а вместо этого в моделе наследованной от базового класса ActiveRecord Вам нужно будет переопределить метод find текущего класса, а выглядеть это будет вот так:

Класс Book (app/models/Book.php)

namespace app\models;

use Yii;
use app\components\ActiveQuery;

/**
 * This is the model class for table "{{%book}}".
 *
 * @property int $id
 * @property string $name
 * @property string $description
 * @property int $state
 */
class Book extends \yii\db\ActiveRecord
{
    // переопределенный метод find
    public function find()
    {
        return new ActiveQuery(get_called_class());
    }
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%book}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['id', 'name'], 'required'],
            [['id', 'state'], 'integer'],
            [['description'], 'string'],
            [['name'], 'string', 'max' => 255],
            [['id'], 'unique'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'name' => 'Name',
            'description' => 'Description',
            'state' => 'State',
        ];
    }
}

Вот и все. На этом данный урок закончен