Шаблон проектирования репозиторий может оказаться очень полезным для вас. Его использование сделает ваш код понятнее. Фактически, этот шаблон проектирования не привязан к конкретному фреймворку. Однако же для этой статьи мы будем использовать Ларавел, чтобы показать как использование репозиториев сделает код контроллеров более наглядным, менее связанным и простыми для чтения. Давайте приступим.

Работа без репозиториев


Использование репозиториев не является обязательным! Разумеется, ваш код может быть достаточно хорош и без использования данного шаблона, но чаще всё будет наоборот. К примеру, если вы не используете репозитории, ваше приложение будет сложнее тестировать и использовать код в других проектах. Давайте посмотрим на примере.
Получаем список объектов из базы данных недвижимости
HousesController.php
<?php
 
class HousesController extends BaseController {
 
 
	public function index()
	{
		$houses = House::all();
		
		return View::make('houses.index', compact('houses'));
	}
 
 
	public function create()
	{
		return View::make('houses.create');
	}
 
 
	public function show($id)
	{
		$house = House::find($id);
		
		return View::make('houses.show', compact('house'));
	}
}

Это достаточно типичный код, в котором используется Eloquent для взаимодействия с базой данных, содержащей списки объектов для продажи. И этот подход работает, но теперь контроллер связан с Eloquent. Если же мы внедрим репозиторий, мы избавимся от связанности. Что сделает возможность использовать этот код в других проектах.

Работа с репозиториями


Необходимо проделать ряд действий, для того, чтобы реализовать шаблон «репозиторий». Однако, поняв этот алгоритм однажды, он покажется довольно логичным. Давайте пройдёмся по каждому пункту
  1. Создадим папку для репозитория
    Вы наверняка ознакомлены с Laravel File Structure. Создадим паку в директории «app» для хранения в ней всех специфических для нашей бизнес логики файлов. Для данного примера мы создадим repotut/repositories в app директории, которая будет содержать наши файлы. Это также определит структуру пространства имён, которую мы будем использовать при создании наших файлов.
  2. Создадим интерфейс
    Следующий шаг – это создание интерфейса, который будет определять методы, которые наш репозиторий должен реализовать. Он просто определяет методы, которые будут представлены в нашем репозитории. Наш интерфейс будет выглядеть следующим образом. Обратите внимание на пространство имён и методы, которые будут использоваться.
    HouseRepositoryInterface.php
    <?php
    namespace repotut/repositories;
     
    interface HouseRepositoryInterface {
    	
    	public function selectAll();
    	
    	public function find($id);
    	
    }
    

  3. Создадим наш репозиторий
    Теперь мы можем создать репозиторий который сделает всю тяжёлую работу за нас. Он находится в файле, в котором мы можем поместить все наши детальные запросы к Eloquent, вне зависимости от того, насколько сложными они будут. Каждый метод просто имеет стандартное имя, то есть в нашем контроллере мы можем просто написать некоторый очень короткий код, чтобы получить желаемый результат.
    DbHouseRepository.php
    <?php
    namespace repotutrepositories;
     
    use House;
     
    class DbHouseRepository implements HouseRepositoryInterface {
    	
    	public function selectAll()
    	{
    		return House::all();
    	}
    	
    	public function find($id)
    	{
    		return House::find($id);
    	}
    }
    

  4. Создадим бэкенд сервис провайдер
    Для нашего контролера мы собираемся определить тип интерфейса. Мы собираемся сделать внедрение зависимости, используя интерфейс Это означает то, что мы должны зарегистрировать интерфейс в Ларавел, чтобы он знал, какую реализацию нашего интерфейса мы хотим использовать. Мы будем использовать сначала Eloquent реализацию, но позже мы поменяем на реализацию, основанную на файлах, чтобы показать гибкость использования данного подхода. Мы поместим этот сервис провайдер в то же пространство имён, что и другие наши файлы.
    BackendServiceProvider.php
    <?php
    namespace repotutrepositories;
     
    use Illuminate/Support/ServiceProvider;
     
    class BackendServiceProvider extends ServiceProvider {
    	
    	public function register()
    	{
    		$this->app->bind('repotut/repositories/HouseRepositoryInterface', 'repotutrepositoriesDbHouseRepository');
    	}
    }
    

    Этот код говорит о том, что когда вы видите контроллер и его входной параметр имеет тип HouseRepositoryInterface, Ларавел поймёт, что вы хотите использовать DbHouseRepository.
  5. Обновим наш массив провайдеров
    Теперь, когда мы создали новый сервис провайдер, мы должны несколько изменить массив провайдеров в файле app/config/app.php. Он может выглядеть следующим образом:
    'providers' => array(
     
    		'IlluminateFoundationProvidersArtisanServiceProvider',
    		'IlluminateAuthAuthServiceProvider',
    		'IlluminateCacheCacheServiceProvider',
    		'IlluminateSessionCommandsServiceProvider',
    		'IlluminateFoundationProvidersConsoleSupportServiceProvider',
    		'IlluminateRoutingControllerServiceProvider',
    		'IlluminateCookieCookieServiceProvider',
    		'IlluminateDatabaseDatabaseServiceProvider',
    		'IlluminateEncryptionEncryptionServiceProvider',
    		'IlluminateFilesystemFilesystemServiceProvider',
    		'IlluminateHashingHashServiceProvider',
    		'IlluminateHtmlHtmlServiceProvider',
    		'IlluminateLogLogServiceProvider',
    		'IlluminateMailMailServiceProvider',
    		'IlluminateDatabaseMigrationServiceProvider',
    		'IlluminatePaginationPaginationServiceProvider',
    		'IlluminateQueueQueueServiceProvider',
    		'IlluminateRedisRedisServiceProvider',
    		'IlluminateRemoteRemoteServiceProvider',
    		'IlluminateAuthRemindersReminderServiceProvider',
    		'IlluminateDatabaseSeedServiceProvider',
    		'IlluminateSessionSessionServiceProvider',
    		'IlluminateTranslationTranslationServiceProvider',
    		'IlluminateValidationValidationServiceProvider',
    		'IlluminateViewViewServiceProvider',
    		'IlluminateWorkbenchWorkbenchServiceProvider',
    		'repotut/repositories/BackendServiceProvider'
     
    	),
    

  6. Обновим наш контроллер для внедрения зависимости
    Мы уже построили прочный фундамент на данном этапе. Мы можем теперь обновить контроллер для облегчения внедрения реализации HouseRepositoryInterface. Это позволит нам удалить все вызовы Eloquent непосредственно в контроллере, и заменить их простым стандартным вызовом метода. Наш обновлённый контроллер будет выглядеть следующим образом:
    HousesController.php
    <?php
     
    use repotutrepositoriesHouseRepositoryInterface;
     
    class HousesController extends BaseController {
    	
    	public function __construct(HouseRepositoryInterface $house)
    	{
    		$this->house = $house;
    	}
     
     
    	public function index()
    	{
    		$houses = $this->house->selectAll();
     
    		return View::make('houses.index', compact('houses'));
    		
    	}
     
     
    	public function create()
    	{
    		return View::make('houses.create');
    	}
     
     
    	public function show($id)
    	{
    		$house = $this->house->find($id);
    		
    		return View::make('houses.show', compact('house'));
     
    	}
    }
    

  7. Убедимся в корректности маршрутов
    Для данного примера мы просто установим маршрут для ресурса следующим образом:
    <?php
     
    /*
    |--------------------------------------------------------------------------
    | Application Routes
    |--------------------------------------------------------------------------
    |
    | Here is where you can register all of the routes for an application.
    | It's a breeze. Simply tell Laravel the URIs it should respond to
    | and give it the Closure to execute when that URI is requested.
    |
    */
     
    Route::resource('houses', 'HousesController');
    

  8. Обновим composer.json
    Если вы не сделали это до сих пор, убедитесь, что пространство имён, на которое мы ссылались, находится в composer.json. Обратите внимание на добавление «psr-4»:{«repotut\»: «app/repotut» }, которое говорит компоузеру сделать автозагрузку классов в пространстве имён repotut.
    {
    	"name": "laravel/laravel",
    	"description": "The Laravel Framework.",
    	"keywords": ["framework", "laravel"],
    	"license": "MIT",
    	"require": {
    		"laravel/framework": "4.2.*"
    	},
    	"autoload": {
    		"classmap": [
    			"app/commands",
    			"app/controllers",
    			"app/models",
    			"app/database/migrations",
    			"app/database/seeds",
    			"app/tests/TestCase.php"
    		],
                
                    "psr-4":{
                         "repotut\": "app/repotut"
                    }
    	},
    	"scripts": {
    		"post-install-cmd": [
    			"php artisan clear-compiled",
    			"php artisan optimize"
    		],
    		"post-update-cmd": [
    			"php artisan clear-compiled",
    			"php artisan optimize"
    		],
    		"post-create-project-cmd": [
    			"php artisan key:generate"
    		]
    	},
    	"config": {
    		"preferred-install": "dist"
    	},
    	"minimum-stability": "stable"
    }
    

    Не забудьте выполнить composer dump после обновления composer.json

Отличная работа, давайте проверим результат в браузере. Мы можем посетить localhost/repotut/public/houses и увидеть 3 объекта для продажи разного цвета.

Простая подмена реализации интерфейса


Давайте представим, что в будущем вы решили, что Eloquent – это не лучший вариант для управления данными в вашем приложении. Нет проблем! Мы уже заложили фундамент для использования инетtрфейса. Вы можете просто создать новый репозиторий и изменить сервис провайдер. Давайте посмотрим. Как мы можем подменить реализацию.
  1. Создадим новый репозиторий
    Мы будем использовать сейчас достаточно простой пример. Давайте представим, что это целый класс для файловой системы, который предоставляет хранилице данных, используя обычные файлы. В нашем случае, мы добавим простую логику, чтобы проверить подмену реализации. Обратите внимание, что этот класс реализует те же самые методы что и версия с Eloquent, которую мы использовали ранее.
    FileHouseRepository.php
    <?php
    namespace repotut/repositories;
     
    class FileHouseRepository implements HouseRepositoryInterface {
    	
    	public function selectAll()
    	{
    		$houses = new stdClass;
    		
    		$house1 = new stdClass;
    		$house1->color = 'Olive';
    		
    		$house2 = new stdClass;
    		$house2->color = 'Yellow';
    		
    		$house3 = new stdClass;
    		$house3->color = 'Brown';
    		
    		$houses = array($house1,$house2,$house3);
    		
    		return $houses;
    	}
    	
    	public function find($id)
    	{
    		return 'Here is a single house listing, again using the file system';
    	}
    }
    

  2. Обновим сервис провайдер
    Теперь перейдём к сервис провайдеру. Всё, что мы должны сделать, так это это изменить одну строку кода! Мы просто говорим фреймворку, что теперь когда мы видим HouseRepositoryInterface, мы хотим использовать FileHouseRepository вместо DbHouseRepository.
    <?php
    namespace repotut/repositories;
     
    use IlluminateSupportServiceProvider;
     
    class BackendServiceProvider extends ServiceProvider {
    	
    	public function register()
    	{
    		$this->app->bind('repotut/repositories/HouseRepositoryInterface', 'repotut/repositories/FileHouseRepository');
    	}
    }
    


  3. Как вы могли заметить, нужно проделать достаточо много шагов, чтобы заставить репозитории работать в вашем приложении. И это действительно так. Но если вам нужна уверенность в том, что поддержка вашего кода в дальнейшем будет проще, потратьте время для правильной архитектуры вашего приложения сейчас. И вы пожнёте результаты этой работы в дальнейшем. Воспринимайте это как форму отложенного вознаграждения, которое в наши дни совсем позабытое искусство.

Комментарии (0)