Сервисный слой и репозитории в yii2.Слоистая архитектура.
  • 3076

Сервисы и репозитории в yii2.

Автор: admin | 03 июля (Вт.) 2018г. в 18ч.54м.

Создание сервисов и репозиториев в Yii2.

Рассмотрим создание класса сервиса и репозитория с контроллером на примере создания простейшей доски обьявлений.
Так как основная цель статьи - показать как и где создавать сервисы и репозитории, использование сервисов в контроллерах, то пусть функционал доски обьявлений будет ограничен для лучшего понимания темы статьи.

Основной функционал нашего приложения:

  • Главная страница index.php для вывода всех объявлений.
  • Страница для добавления обьявления.
  • Страница для удаления объявления.

Файловая структура приложения.

За основу возьмем advanced шаблон yii2, хотя для примера особой разницы нет. Отмечу только создаваемую структуру
файлового дерева, чтобы не терять суть.
-backend
-common
-frontend
	-config
		-main.php
		-bootstrap
			-BoardBootstrap.php
	-controllers
		-BoardController.php
	-services
		-BoardService.php
	-storages
		-StorageInterface.php
		-BoardDaoStorage.php
	-views
		-board
			-index.php
			-_list.php
			-create-ad
			-delete-ad​

Создание класса сервиса BoardService.php

<?php
namespace frontend\services;

use frontend\storages\StorageInterface;

class BoardService
{
	private $storage;

	private $_items = [];

	public function __construct(StorageInterface $storage)
	{
		$this->storage = $storage;
	}

	public function createAds($title, $message)
	{
		$dto = ['title' => $title, 'message' => $message];
		$this->storage->save($dto);
	}

	public function remove($id)
	{
		$this->storage->delete($id);
	}

	public function getItems()
	{
		$this->loadItems();
		return $this->_items;
	}

	private function loadItems(){
		$this->_items = $this->storage->load();
	}

}​

Данный класс является сервисным для контроллера BoardController, который будет создан далее.
Сервисный класс (слой) в данном случае служит посредником между контроллером и репозиторием.

Создание класса репозитория.

Сначала создадим интерфейс репозитория, в котором определим основные методы для работы с хранилищем.
<?php

namespace app\cart\storage;

interface StorageInterface
{
	/**
	* @return array
	*/
	public function load();

	/**
	* @param array
	*/
	public function save(array $items);
	/**
	* @param int
	*/
	public function delete($id;

}​

Теперь создадим класс хранилища (репозитория) class BoardDaoStorage, который будет реализовывать интерфейс StorageInterface.

<?php

namespace frontend\storage;

use yii\db\Connection;

class BoardDaoStorage implements StorageInterface
{
	
	const TABLE_NAME = 'board';

	private $connection;
	
	public function __construct(Connection $connection)
	{
		$this->connection = $connection;
	}

	public function load()
	{
		return $connection->createCommand('SELECT * FROM board')->queryAll();
	}

	public function save(array $items)
	{
		$connection->createCommand()->insert('user', $items)->execute();
	}

	public function delete($id)
	{
		$connection->createCommand()->delete(self::TABLE_NAME, "id = $id")->execute();
	}
}
​

В классе репозитория мы будем применять Yii2 DAO (объекты доступа к данным), поэтому класс назвали соответственно BoardDaoStorage.
В свойстве $connection хранится экземпляр соединения с базой данных, который передается через конструктор.

Конфигурация зависимостей в DI Container yii2.

Создадим файл предварительной загрузки для регистрации зависимостей в di контейнире. Для этого в папке config
создадим BoardBootstrap.php который обязательно должен реализовать BootstrapInterface:
<?php


namespace frontend\config\bootstrap;

use yii;
use yii\base\BootstrapInterface;
use yii\di\Container;
use frontend\storages\BoardDaoStorage;
use frontend\services\BoardService;

/**
 * BoardBootstrap
 */
class BoardBootstrap implements BootstrapInterface{
    
    public function bootstrap($app){
        
        $container = \Yii::$container;
        
        $container->setSingleton('BoardService');

		$container->set('frontend\storages\StorageInterface', function() {
			return new BoardDaoStorage(Yii::$app->db);
		});

		//или так
		//Выбираем один вариант регистрации хранилища, а другой комментим
		$container->set('frontend\storages\StorageInterface',[
        		'class' => 'BoardDaoStorage',
        		'connection' => Yii::$app->db,
        	]);
	}
    
}
​
Здесь я для примера показал два варианта регистрации BoardDaoStorage в контейнере зависимостей. Какой выбрать, все
зависит от логики приложения и индивидуальных задач.

Сервис BoardService регистрируем через 'одиночку' setSingleton('BoardService') так как нам нет необходимости
создавать разные объекты этого класса, в отличие от класса хранилища (репозитория) BoardDaoStorage, который
может принимать в качестве аргумента конструктора разные виды соединений Yii::$app->db.

Теперь нужно зарегестрировать наш файл предзагрузки в конфигурации main.php, добавив его в массив bootstrap:
return [
    'id' => 'app-frontend',
    'basePath' => dirname(__DIR__),
    'bootstrap' => [
        
        'frontend\config\bootstrap\BoardBootstrap',
    ],

    //........​
Все зависимости установлены. Теперь перейдем к контроллеру.

Создание контроллера.

<?php

namespace frontend\controllers;

use frontend\services\BoardService;
use app\models\CreateAdsForm;
use Yii;
use yii\data\ArrayDataProvider;
use yii\filters\VerbFilter;
use yii\web\Controller;

class BoardController extends Controller
{
	private $service;

	public function __construct($id, $module, BoardService $service, $config = [])
	{
		$this->service = $service;
		parent::__construct($id, $module, $config);
	}

	public function behaviors()
	{
		return [
			'verbs' => [
				'class' => VerbFilter::className(),
				'actions' => [
					'delete' => ['post'],
				],
			],
		];
	}

	public function actionIndex()
	{
		$dataProvider = new ArrayDataProvider([
			'allModels' => $this->service->getItems(),
		]);

		return $this->render('index', [
			'dataProvider' => $dataProvider,
		]);
	}

	public function actionCreate()
	{
		$form = new CartAdsForm();
		if ($form->load(Yii::$app->request->post()) && $form->validate()) {
			$this->service->createAds($form->title, $form->message);
			return $this->redirect(['index']);
		}

		return $this->render('create', [
			'model' => $form,
		]);
	}

	public function actionDelete($id)
	{
		$this->service->remove($id);
		return $this->redirect(['index']);
	}

}​

Создание модели формы.

Мы хотели бы пользоваться всеми благами валидации форм, которые поддерживает yii2, поэтому создадим модель
для обработки формы создания объявления:
<?php

namespace app\models;
use yii\base\Model;

class CartAddForm extends Model
{
	public $productId;
	public $amount;
	public function rules()
	{
		return [
			[['title', 'message'], 'required'],
			[['title', 'message'], 'string'],
		];
	}
}​

Создание файлов видов.

Шаблон для главной страницы.

Создадим файл views/index.php для главной страницы доски объявлений. Применим виджет ListView, который будет принимать ArrayDataProvider переданный из контроллера:
<?php

use yii\widgets\ListView;
use yii\helpers\Html;

/* @var $this yii\web\View */
/* @var $dataProvider yii\data\ArrayDataProvider */
$this->title = 'Доска объявлений';
$this->params['breadcrumbs'][] = $this->title;
?>

<div class="border-index">
<h1><?= Html::encode($this->title) ?></h1>
<p><?= Html::a('Создать объявление', ['create'], ['class' => 'btn btn-success']) ?></p>

<?= ListView::widget([
	    'dataProvider' => $dataProvider,
	    'itemView' => '_list',
	]);
?>

</div>​
И создаем шаблон _list.php для ListView:
<?php
use yii\helpers\Html;
use yii\helpers\HtmlPurifier;
?>
 
<div class="news-item">
    <h2><?= Html::encode($model->title) ?></h2>    
    <?= HtmlPurifier::process($model->message) ?>    
    <p><?= Html::a('Удалить объявление', ['delete', 'id' => $model->id], ['class' => 'btn btn-success', 'data-method' => 'POST']) ?></p>
</div>​

Шаблон для формы создания объявления create.php:

<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model app\models\CartAddForm */

$this->title = 'Создать объявление';
$this->params['breadcrumbs'][] = ['label' => 'Все объявления', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>

<div class="ads-create">
	<h1><?= Html::encode($this->title) ?></h1>

	<?php $form = ActiveForm::begin(['id' => 'contact-form']); ?>
	<?= $form->field($model, 'title') ?>
	<?= $form->field($model, 'message') ?>
	<div class="form-group">
		<?= Html::submitButton('Создать', ['class' => 'btn btn-primary']) ?>
	</div>
	<?php ActiveForm::end(); ?>
</div>​

На простом примере мы рассмотрели как использовать классы сервисов и репозиториев в yii2, чтобы сделать код
более чистым.

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

Приветствую!

Меня зовут Сергей. Я - автор этого блога.

Если Вам был полезен материал на моем сайте, поддержите пожалуйста мой проект, чтобы о нем узнали другие люди - кликните plizz :) на иконку в соц. сети, чтобы поделиться материалом с другими.