Laravel Core Model (Laracore)
A repository, factory, and criteria layer for Eloquent models,
providing a convenient repository interface that still allows
fully-featured, eloquent usage. Allows convenient testing and
dependency injection without sacrificing features or versatility., (*1)
, (*2)
Requirements
Installation
composer require jamesaspence/laravel-core-model
Usage
Laracore (Laravel Core Model) is very simple to use.
It works exactly the same as the Eloquent models
you're already used to. This means you can use all
the model queries you're already used to., (*3)
//First we instantiate the repository
$repository = new ModelRepository();
//We pass in the reference to the model class
//so we can query it
$repository->setModel(User::class);
//Now we use the repository to find a model
$user = $repository->find(1);
//Let's save some new attributes
$repository->fill($user, [
'name' => 'Test Testerton'
]);
//Finally, let's save!!
$repository->save($model);
More advanced queries are allowed as well,
including custom queries. Assuming the same
repository as before:, (*4)
//Let's query based on email AND name
$user = $repository
->query()
->where('name', '=', 'Test Testerton')
->where('email', '=', 'test@test.test')
->first();
The repository's methods map to the model's
methods, allowing, in the above example, a
return of a query builder. This means we don't
lose any of the features we've come to love from
Eloquent., (*5)
"Magic" Methods
Laracore's repositories support the calling of
magic methods, such as local scope queries. For
example, consider the following code:, (*6)
$model = User::active()->get();
You do not need to define a custom repository
with this method hardcoded., (*7)
$repository = new ModelRepository(User::class);
$model = $repository->active()
->get();
Instead, we can call our scope queries and other
magic methods directly on the repository. The
repository will delegate them on to the model
class., (*8)
Our magic method handling also listens for a model
instance being the first argument of a magic method
called via this repository. If the first argument is
an instance of a model, it will instead call the method
on the model instance itself! See the below example:, (*9)
//This
$model = new User();
$repository->doThing($model, $stuff, $things);
//Is equivalent to this
$model->doThing($stuff, $things);
This is meant to catch missed repository methods that we would
want implemented. If this causes issues, feel free to reach out
via the issues on this repository!, (*10)
Relations
Laracore also allows retrieval of relations., (*11)
$user = $repository
->with(['tokens', 'profile'])
->find(1);
//Let's also load a relation with an existing model.
$repository->load($existingUser, 'comments');
ModelRepository classes have a RelationRepository
set which allows even more advanced relation settings,
such as sync and associate., (*12)
//You can also pass in the class definition into the constructor.
$profileRepository = new ModelRepository(Profile::class);
$profile = $profileRepository->newModel(['stuff' => 'things']);
//$repository is still set for User::class here
$user = $repository->find(1);
//Assuming a BelongsTo relation named profile()
//on User, let's associate it!
$repository
->getRelationRepository()
->associateRelation($user, 'profile', $profile);
//Dont forget to save!
$repository->save($user);
//Assuming comment IDs...
$commentIds = [1, 2, 3];
//Let's sync them to a comments relation!
$repository
->getRelationRepository()
->sync($user, 'comments', $commentIds);
All relation methods should be represented as well,
allowing versatile use., (*13)
Dependency Injection
One of the best aspects of this library is the
ability to dependency inject your database access,
rather than using static methods., (*14)
// Rather than doing this... bad!!
public function badControllerMethod()
{
$user = User::find(1);
}
//We can do this! Good!
public function goodControllerMethod(ModelRepository $repository)
{
$repository->setModel(User::class);
$user = $repository->find(1);
}
This allows easy dependency injection, which in
turn makes it very easy to isolate dependencies
for testing., (*15)
Model Factories
Want to create models without using new Model all over your code? ModelFactory is here to help!, (*16)
$factory = new ModelFactory();
//We need to pass in a ModelRepository
//to be able to save
$factory->setRepository(new ModelRepository(User::class));
$user = $factory->make([
'name' => 'Test Testerton'
]);
This will save the model with the attributes specified., (*17)
You can also use the ModelFactory to save BelongsTo
relations:, (*18)
$user = $factory->make([
'name' => 'Test Testerton'
], [
'profile' => $profile
]);
Inheritance
Another nice feature is the ability to extend
these classes at will. You can continue to use
ModelRepository on its own, but if you prefer,
you can extend the repositories and factories yourself., (*19)
Here, we'll extend ModelRepository so we don't have to
set the model every time. We'll also make it so default
criteria are set on the repository., (*20)
class UserRepository extends ModelRepository
{
/**
* {@inheritdoc}
*/
public function getDefaultModel()
{
//We just need to pass in our default model
return User::class;
}
}
Then, we can use this without setting a model!
No setModel required!, (*21)
public function controllerMethod(UserRepository $repository)
{
$user = $repository->find(1);
}
This will perform the following query (if using MySQL):, (*22)
SELECT * FROM `users` WHERE `name` = ? AND `id` = ?
with the two bound parameters of 'Test' and '1'., (*23)
Future Plans
Short term, the plan is to keep this library compatible with major
versions of Laravel > 5. That means testing for new versions and
adding new methods that exist in newer versions., (*24)
I would love to add non-eloquent support to this repository.
The plan is to add both raw query as well as Doctrine repositories,
but that isn't coming quite yet., (*25)
Long-term plans are a little more unclear. After non-eloquent support,
I will probably decide on my next feature to implement. If you have any
ideas, I would love to hear them!, (*26)