To keep it simple, let's suppose an application which has Accounts and Users. Each account may have any number of users. There's also 3 consumers of UserRepository:
An admin interface which may list all users
Public front-end which may list all users
An account authenticated API which should only list it's own users
Assuming UserRepository is something like this:
class UsersRepository extends DatabaseAbstraction {
private function query() {
return $this->database()->select('users.*');
}
public function getAll() {
return $this->query()->exec();
}
// IMPORTANT:
// Tons of other methods for searching, filtering,
// joining of other tables, ordering and such...
}
Keeping in mind the comment above, and the necessity to abstract user querying conditions, How should I handle querying of users filtering by account_id? I can picture three possible roads:
1. Should I create an AccountUsersRepository?
class AccountUsersRepository extends UserRepository {
public function __construct(Account $account) {
$this->account = $account;
}
private function query() {
return parent::query()
->where('account_id', '=', $this->account->id);
}
}
This has the advantage of reducing the duplication of UsersRepository methods, but doesn't quite fit into anything I've read about DDD so far (I'm rookie by the way)
2. Should I put it as a method on AccountsRepository?
class AccountsRepository extends DatabaseAbstraction {
public function getAccountUsers(Account $account) {
return $this->database()
->select('users.*')
->where('account_id', '=', $account->id)
->exec();
}
}
This requires the duplication of all UserRepository methods and may need another UserQuery layer, that implements those querying logic on chainable way.
3. Should I query UserRepository from within my account entity?
class Account extends Entity {
public function getUsers() {
return UserRepository::findByAccountId($this->id);
}
}
This feels more like an aggregate root for me, but introduces dependency of UserRepository on Account entity, which may violate a few principles.
4. Or am I missing the point completely?
Maybe there's an even better solution?
Footnotes: Besides permissions being a Service concern, in my understanding, they shouldn't implement SQL query but leave that to repositories since those may not even be SQL driven.
Fetching all users belonging to an account is more of an UI concern. My suggestion is use your MVC controller(like AccountAdminController?) invoke the UserRepository.findByAccountId() directly.
I think Aggregates should be returned only by its own repository.
Related
I read some articles about repository pattern and I want to know the reason why the constructor is needed when I can directly call the Model and return the data? I also think that Book::all(); is less code than $this->model->all(). Is it just a good practice or it has some purpose?
class BookRepository implements RepositoryInterface {
private $model;
public function __construct(Book $model)
{
$this->model = $model;
}
public function index()
{
return $this->model->all();
}
}
and
class BookRepository implements RepositoryInterface {
public function index()
{
return Book::all();
}
}
The primary reason is Inversion of Control, basically letting your application determine what should be provided to fulfill that dependency. The reason this is important is, in the event you decide to refactor that code, you can simply tell Laravel to load a different implementation. No code need be altered in the Repository itself.
This however leads into the idea of not using classes directly, and using interfaces instead to declare your dependancies. That way any implementation can be swapped out and your code remains readable.
class BookRepository {
public function __construct(BookInterface $book)
{
$this->book = $book;
}
}
Now your Repository doesn't really care about the actual class, just that it implements the book interface which enforces a specific set of methods be defined. An example of the benefit is if you're using, say, MySQL as a database for your Book but switch to Postgres you may need to significantly change the underlying code but want to keep both implementations for legacy reasons. You can easily tell Laravel to load your standard Book class, or your new PostgresBook class because both still implement the BookInterface.
Your Repository doesn't need to change at all. Just add a bind and you're good.
Another more direct example is if you decided you wanted to switch from Eloquent to ActiveRecord.
Both will work but if for any reason you want to change the model class [Book] with any other model for example [MyBook] so in this case, you will change only the constructor parameter, not all the functions which use [Book]
public function __construct(MyBook $model)
{
$this->model = $model;
}
let say that I have something like this:
class User{
/**
* Object that is lazy loaded
*/
private $statistic; //object with some stored data and some calculated data
}
Some of the $statistic's properties are stored in the DB but some other of them are calculated by analyzing the user activity (querying data records).
the thing is that I got a $user and when I run $user->getStatistic() as spected, I get the stored $statistic data and I need to add more data using sql queries and I don't know where to program this functionality.
¿overriding the Repository? I try overriding the find() method but it doesn't work
I know that if I use the active record pattern this can be done with no problem giving that I can access the DB in the construct method or the getters maybe, etc.
but I don't know how this could be done with doctrine standard behavior.
I believe that there must be a way to ensure that every instance of the Statistic Class have this calculated data on it.
I'm using symfony... maybe a service or something...
There are a number of ways to solve your problem.
Doctrine listener
This is probably the easiest one. Use Doctrine postLoad event to fill out data you need on your User model.
This is a completely valid approach, but has a couple of drawbacks:
This will be ran every time doctrine fetches an User entity instance from database. If it fetches a list of 100 users, it will be ran 100 times. This could make a performance problem if you do some time-consuming tasks in there.
They are hard to debug: if an error is encountered, events usually make code flow a lot less clear and therefore make debugging harder. If you do simple stuff, and don't overuse them, then it's probably fine, otherwise think about other options.
Abstracting away doctrine
I'm strongly in favor of this approach and I use it in almost every project.
Even though I'm one of the people who try to have the least amount of layers and indirection necessary, I do think that wrapping data persistence into your own services is a good idea. It isolates rest of your system from having to know how your data is stored.
I suggest not using Doctrine repositories/Entity manager directly. Instead, wrap them in your own services.
This makes your persistence API squeaky clean and obvious, while giving you ability to manipulate your models before they reach your business logic.
Here is an example of how I would approach your problem:
# src/AppBundle/Repository/UserRepository.php
class UserRepository
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function findById($userId)
{
$user = $this->em->getRepository(User::class)->find($userId);
$this->calculateUserStatistics($user);
return $user;
}
public function save(User $user)
{
$this->em->persist($user);
$this->em->flush();
}
// ...
private function calculateUserStatistics(User $user)
{
// calculate and set statistics on user object
}
}
This approach has a number of advantages:
Decoupling from Doctrine
Your business code is no longer coupled to Doctrine, it doesn't know that Doctrine repositories/entity manager exist at all. If need arises, you can change UserRepository implementation to load users from remote API, from file on disk....from anywhere.
Model manipulation
It allows you to manipulate your models before they get to business logic, allowing you to calculate values not persisted as a field in database. Eg, to fetch them from Redis, from some API or other...
Cleaner API
It makes it really obvious what abilities your system has, making understanding easier and allowing easier testing.
Performance optimisation
It doesn't suffer from performance issues as first approach. Take the following example:
You have $eventsCount field on your User model.
If you load list of 100 users and use first approach, you would need to fire 100 queries to count number of events belonging to each user.
SELECT COUNT(*) FROM events WHERE user_id = 1;
SELECT COUNT(*) FROM events WHERE user_id = 2;
SELECT COUNT(*) FROM events WHERE user_id = 3;
SELECT COUNT(*) FROM events WHERE user_id = 4;
...
If you have your own UserRepository implementation, however, you can just make method getEventCountsForUsers($userIds) which would fire one query:
SELECT COUNT(*) FORM events WHERE user_id IN (:user_ids) GROUP BY user_id;
You can implement your own repository to include your own sql queries, Symfony have documented it pretty well in their documentation, see here.
Here's how I've done it previously using annotations (this can be done via yaml too, just check the link above)...
Entity:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User
{
// ...
private $statistic;
// ...
}
User Repository:
namespace AppBundle\Entity;
use Doctrine\ORM\EntityRepository;
class ProductRepository extends EntityRepository
{
public function getUserStats()
{
// Use query builder to build your query for stats here
}
}
Since your custom repository is extending the EntityRepository, you will still have access to Doctrines lazy load methods (find, findBy, findAll etc...)
Maybe you don't need exactly User instance.
You can use NEW() syntax in DQL.
class UserStatDTO
{
private $user;
private $statistic;
private $sum;
public function __construct(User $user, $statistic, $sum)
{
$this->user = $user;
$this->statistic = $statistic;
$this->sum = $sum;
}
public function getUser()
{
return $this->user;
}
public function getSum()
{
return $this->sum;
}
public function getStatistic()
{
return $this->statistic;
}
}
class UserRepository
{
public function getUsersWithCalculatedStat()
{
return $this->getEntityManager()->createQuery('
SELECT NEW UserStatDTO(
u, u.statistic, u.count1 + u.count2
) FROM User
')->getResult();
}
}
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#new-operator-syntax
I'm reworking a project on Laravel 5.1
What I realize is that the old classes have become much complicated and do not really follow the 'single responsibility' principle anymore.
So I'm planning to do such:
<?php
class User extends Model
{
}
class SocialUser extends User
{
}
So I have a few questions,
Is it possible to achieve that?
If yes, then does the SocialUser class link back to the same database table which is Users and would it conflict with the User model itself?
Is this all a good design practice at the first place? Or I better make use of traits?
Thank you.
What you’re doing (extending the User model) is perfectly fine, and an approach I use myself in projects.
For example, if an application I’m building has shop-like functionality, then I may create a Customer model that extends my User model, and contains say, order-related relations:
class Customer extends User
{
public function orders()
{
return $this->hasMany(Order::class, 'customer_id');
}
public function worth()
{
return $this->orders()->sum(function ($order) {
return $order->total();
});
}
}
In a recent project, I’ve been working on email campaign functionality and created a Recipient class that extends the User model to add campaign-related methods:
class Recipient extends User
{
public function campaigns()
{
return $this->belongsToMany(Campaign::class, 'recipient_id');
}
}
Because both of these classes extend the User model, I get all of those (and Eloquent) methods:
$customers = Customer::with('orders')->get();
So long as you set the table in your base User model, any classes that inherit it will use that same table, even though the model may be named differently (i.e. Customer, Recipient, Student etc).
IMHO I would go for the Repository pattern. It make's a lot of sense in your situation.
I would do the following:
interface UserRepository {
public function find($id);
public function getAll();
public function create(array $attributes);
public function destroy($id);
//you get the point
}
class CoreUserRepository implements UserRepository
{
//implement the interface rules
}
class SocialUserRepository extends CoreUserRepository
{
//implement the specific logic related to a SocialUser
}
Update
As Mjh described in the comments simply implementing the interface on all UserTypeRepository caused repetition - probably not what you want!
By extending your CoreUser you avoid repetition & maintain a design that will work for your situation.
Although, in your case it could be argued that you are still following SRP because everything in the User model is relating to a user, it's only the type of user which is differing.
Why go for the Repository Pattern?
You are ensuring you have a contractual agreement that all User
Repositories need to implement.
Code is easier to maintain.
Business and data access logic can be tested separately
Should you extend your User model?
Here you are in danger of model pollution. While you can do anything with a model - not everything is a good idea.
Defining relationships on this approach would be a headache due to the confusion caused.
Lets see my architect:
Model:
// links table: (ID, LINKNAME)
Class Link extends Link_base
{
}
Controller:
public function index()
{
$this->links = new Doctrine - here I build the query, SELECT, ORDER BY, etc
}
in this example, the model can be remain empty (no serious logic), all I need is a select with an order by. Im not sure I can use Doctrine in controller though - should I remake it like this?
Class Link extends Link_base
{
public function getLinks()
{
return new Doctrine - here I build the query, SELECT, ORDER BY, etc;
}
}
Controller:
public function index()
{
$this->links = Links::getLinks();
}
Im not sure which way seems to be OK. Of course, when selecting needs a more complex, formatting todo-s, it goes to the model or helper - but I feel like I just made a new (unnecessary) layer. This getLinks() used only once. In other words: Doctrine may be only used in model, or can it be used in controllers too?
Your entities (or models if you prefer that name) should not know how they are saved to / retrieved from the database. They should just be simple PHP objects, only containing a number of properties (corresponding to the database columns) and their getters and setters.
(If you are interested, read a bit about the single responsibility principle which states that every class should have one, and only one responsibility. If you make your entities both responsible for storing data and knowing how to save that data in the database, you will have a greater chance of introducing bugs when one of those things changes.)
You can fetch entities from inside your controller:
<?php
namespace Your\Bundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class LinkController extends Controller
{
public function fooAction()
{
$links = $this->getDoctrine()
->getRepository('YourBundle:Link')
->findAll();
// do something with the result, like passing it to a template
}
}
However, you might need a more complex query (that includes sorting and filtering) and you might need to run that query from multiple controllers. In that case, you don't want to duplicate that logic to multiple controllers, you want to keep that logic in one central place.
To do so, create a repository:
<?php
namespace Your\Bundle\Repository;
use Doctrine\ORM\EntityRepository;
class LinkRepository extends EntityRepository
{
public function findAllOrderedByName()
{
return $this->getEntityManager()
->createQuery(
'SELECT l FROM YourBundle:Link l ORDER BY l.name ASC'
)
->getResult();
}
}
And add the repository class to your mapping:
Your\Bundle\Entity\Link:
type: entity
repositoryClass: Your\Bundle\Repository\LinkRepository
(check the Symfony's documentation about custom repositories if you're using XML or annotations instead of Yaml)
Now in your controller, you can simply update your fooAction method so it uses your custom repository method:
public function fooAction()
{
$links = $this->getDoctrine()
->getRepository('YourBundle:Link')
->findAllOrderedByName();
}
For more information, Symfony's documentation includes a great article about Doctrine. If you haven't done so already, I'd definately recommend reading it.
I have two database connections, one that is used for most of my application data, and one that is only used for reads.
Although I can setup my database user account to only allow reads, there are other people administering this system, and I want some redundancy at the application level to absolutely prevent unintended writes using the Yii's standard ActiveRecord classes.
Found this bit of information on the forums, but was wondering if someone could confirm that this is a good approach and/or suggest another one.
public function onBeforeSave($event)
{
$this->db = Yii::app()->masterDb;
}
public function onAfterSave($event)
{
$this->db = Yii::app()->db;
}
http://www.yiiframework.com/forum/index.php/topic/5712-active-record-save-to-different-server-load-balancefail-over-setup/
Per that link you provided to the Yii forums, there's an extension that handles this for you:
http://www.yiiframework.com/extension/dbreadwritesplitting
I'd probably look into that first, if you've got a lot of AR models. You could go the Behavior route (as suggested in that forum post) as another option.
But whatever you do, you are going to want to be overriding beforeSave / afterSave instead of onBeforeSave / onAfterSave. Those methods are for triggering events, not just running your own special code. And, per another one of the forum posts, you'll need to set your AR db variable using a static call. So Sergey's code should actually be:
class MyActiveRecord extends CActiveRecord
{
...
public function beforeSave()
{
// set write DB
self::$db = Yii::app()->masterDb;
return parent::beforeSave();
}
public function afterSave()
{
// set read db
self::$db = Yii::app()->db;
return parent::beforeSave();
}
...
}
class User extends MyActiveRecord {}
class Post extends MyActiveRecord {}
...
Given a scenario where your slave can't update with the master, you might run into problems.
Because after updating data you'll maybe read from an old version.
While the given approaches in the forum are very clean and written by authors which are mostly Yii wizards. I also have an alternative. You may override the getDbConnection() method in AR like
public function getDbConnection(){
if (Yii::app()->user->hasEditedData()) { # you've got to write something like this(!)
return Yii::app()->masterDb;
} else {
return Yii::app()->db;
}
}
But you still have to be careful when switching database connections.
class MyActiveRecord extends CActiveRecord
{
...
public function onBeforeSave($event)
{
// set write DB
$this->db = Yii::app()->masterDb;
}
public function onAfterSave($event)
{
// set read db
$this->db = Yii::app()->db;
}
...
}
class User extends MyActiveRecord {}
class Post extends MyActiveRecord {}
...
You have to try that way. But in my opinion, it's not good enough. I think there will be some bugs or defects.