Computers">
Repositório Laravel
Repositório Laravel
Repositório Laravel
Isso mesmo que voc leu, repositrios fazem parte da camada de servios de um sistema.
Eles so uma classe/categoria de servio. Quando se esta estudando design
patterns esbarramos muito em services, se voc entendeu bem como servios funcionam
sabe que neles que concentramos nossas regras de negcio.
Quando falamos de regras de negcio tambm estamos falando de acesso a dados e/ou
entidades, que basicamente so nossos models. Repositrios existem para que o acesso e
interao com nossos models no seja feito diretamente em outras partes do sistema
como controllers.
Repositrios so os servios da camada de acesso e interao com as entidades do banco
de dados.
No mundo real, dificilmente voc muda de ORM no meio do projeto, muito menos de
banco de dados, j que a maioria dos ORMs j do suporte a essa troca de banco de dados.
O maior problema seria trocar de paradigma de banco de dados, de relacional para no
relacional (NoSQL).
Quantas vezes voc trocou um banco de dados do projeto? Quantas vezes voc trocou
de ORM em um projeto? Quantas vezes voc migrou de MySQL/PostgreSQLpara MongoDB?
Eu apostaria que na maioria esmagadora dos projetos nenhuma dessas situaes
aconteceu, das vezes que isso aconteceu apostaria que a troca para um banco de
dados NoSQL quase inexistente.
Se isso uma realidade, porque ento usar repositrios???
Com o tempo essa maravilha tambm se tornou um problema, muitas regras de negcio
(leitura e escrita) acabam ficando espalhadas ou melhor jogadas
Acima de qualquer conceito ou filosofia que repositrios possam ter eles devem agregar valor
e qualidade ao projeto. Isso se traduz em facilidade de manuteno!
Como comear a usar repositrios
Sabe criar uma classe? Ento voc est apto a criar um repositrio.
<?php
class UsersRepository
{
public function getList($limit = 15, $offset = 0)
{
return mysql_query("SELECT * FROM `users` LIMIT {$offset} , {$limit}");
}
}
Sem firulas ou boas prticas, este cdigo apenas para provar um ponto: No precisa de
muito para criar uma classe de repositrio.
Somente aps o conceito ser entendido podemos falar de S.O.L.I.D., DRY, PSRs ou Clean Code.
Agora vamos ao Laravel
O Laravel dispensa apresentaes e o Eloquent (seu ORM) tambm, h muita
documentao e contedo sobre isso. O ponto que importa aqui organizao.
Como dito anteriormente, usar nosso model diretamente pode (e provavelmente
vai) espalhar nossas regras ao vento. Os repositrios existem para resolver esse problema.
Por isso partimos da premissa que:
Somente no repositrio executaremos consultas e interaes no banco de dados
Com isso em mente e a ideia de que no precisamos ser conservadores podemos fazer
MUITAS coisas.
Seja genrico antes de ser especifico
Um repositrio seria muito parecido com isso:
<?php
namespace App\Domains\Users;
class UsersRepository
{
public function create(array $data)
{
return User::create($data);
}
<?php
$query = User::query();
$query->where('email', 'jon.snow@westeros.com');
$query->where('is_dead', false);
$user = $query->first();
$modelClass = User::class;
$query = app($modelClass)->newQuery();
$query = app()->call('\App\Domains\Users\User@newQuery');
$query = app()->call($modelClass, [], 'newQuery');
$query = app()->call(User::class, [], 'newQuery');
namespace App\Support\Repositories;
/**
* @return EloquentQueryBuilder|QueryBuilder
*/
protected function newQuery()
{
return app($this->modelClass)->newQuery();
}
/**
* @param EloquentQueryBuilder|QueryBuilder $query
* @param int $take
* @param bool $paginate
*
* @return EloquentCollection|Paginator
*/
protected function doQuery($query = null, $take = 15, $paginate = true)
{
if (is_null($query)) {
$query = $this->newQuery();
}
if (true == $paginate) {
return $query->paginate($take);
}
return $query->get();
}
/**
* Returns all records.
* If $take is false then brings all records
* If $paginate is true returns Paginator instance.
*
* @param int $take
* @param bool $paginate
*
* @return EloquentCollection|Paginator
*/
public function getAll($take = 15, $paginate = true)
{
return $this->doQuery(null, $take, $paginate);
}
/**
* @param string $column
* @param string|null $key
*
* @return \Illuminate\Support\Collection
*/
public function lists($column, $key = null)
{
return $this->newQuery()->lists($column, $key);
}
/**
* Retrieves a record by his id
* If fail is true $ fires ModelNotFoundException.
*
* @param int $id
* @param bool $fail
*
* @return Model
*/
public function findByID($id, $fail = true)
{
if ($fail) {
return $this->newQuery()->findOrFail($id);
}
return $this->newQuery()->find($id);
}
}
namespace App\Domains\Users;
use App\Support\Repositories\BaseRepository;
use Carbon\Carbon;
$query = $this->newQuery();
$query->where('is_subscriber', true);
$query->where('subscription_ends_in', '<=', $now);
$query->orderBy('name');
namespace App\Domains\Clients
use Artesaos\Warehouse\BaseRepository;
A rea administrativa j possui uma srie de repositrios que por sua vez tem muitos
mtodos que so reaproveitados na rea do tenant. Para no reimplementar todos esses
repositrios novamente e no criar monstrinhos, cria-se repositrios que estendem os
repositrios originais. Assim por herana eles possuem os mesmos mtodos que os
repositrios de administrao. E como toda consulta feita criando um novo
objeto Eloquent\Builder pelo mtodo newQuery basta modificar o comportamento desse
mtodo.
A mesma tcnica pode ser aplicada quando se cria uma nova entidade, definindo
o tenant_id dela automaticamente. Para deixar as coisas mais fceis ainda, toda essa lgica
feita em um trait. O resultado voc confere abaixo.
<?php
namespace App\Domains\Tenants\Repositories\Users;
namespace App\Domains\Tenants\Repositories\Traits;
use App\Domains\Tenants\Tenant;
trait RegularTenantRepository
{
/**
* @var Tenant
*/
protected $tenant;
/**
* @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder
*/
public function newQuery()
{
$query = parent::newQuery();
/**
* @param array $data
*
* @return \Illuminate\Database\Eloquent\Model
*/
public function factory(array $data = [])
{
$model = parent::factory($data);
$model->tenant_id = $this->tenant->id;
return $model;
}
}
Este post ficou bem maior do que eu esperava, porm no quis dividir ele em duas partes,
pois poderia perder parte do foco. Ainda h mais contedo a ser tratado como por
exemplo iterfaces e bind de classes, porm isso ter que ficar para outro momento.