PHP Pattern - how to retrieve collection of objects - php

Situation:
There is simple User class which is Doctrine entity (I skipped comments to keep the code short):
class User {
protected $iUserId;
protected $sName;
}
Question:
How to retrieve collection of objects of class User from the controller?
Follow up:
Until now, we were creating methods like getUsers() in User class which gets data from DB, creates User class objects and returns it.
Now, I'm wondering if it's not a better solution to create class like UserCollection which would handle data retrieving and creating User objects? Maybe I should make use of \Doctrine\Common\Collections\Collection class somehow?
What I'd like to accomplish is easy way to handles i.e. where clauses. UserCollection class could have QueryBuilder object on which I could operate from controller like this:
$oUserCollection = new UserCollection();
$oUserCollection->setWhere( 'u.iUserId = 1' );
$aUsers = oUserCollection->getUsers();
...
Please share your thoughts on that topic.
Update:
Doctrine provides a concept of repositories of entities which I think maybe the solution I'm looking for.

You have two options:
If your criterias are simple, eg. you just want to filter on a single property, or you don't need filtering at all, you can use the findBy() and findAll() methods, on the EntityRepository:
// find all users
$repository = $em->getRepository('My\\User');
$users = $repository->findAll();
// find users who are marked as active
$users = $repository->findBy(array('active' => true));
// sort users by age
$users = $repository->findBy(array(), array('age' => 'DESC'));
If you have complex(er) requirements, or your finders will be used from multiple places, you will have to create a custom EntityRepository, and group your finder logic there. You have to specify in the mapping that you want to create your own EntityRepository for this entity, the method for this varies depending on what mapping driver you use (annotation, yaml, xml). For example, in yaml you would have to place this line in your mapping:
RepositoryClass: My\Repository\UserRepository
And then create the file:
namespace My\Repository;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository {
public function findVIPUsers() {
$query = $this->_em->createQuery("Your query here");
return $query->getResult();
// you can also use $this->findBy(), or $this->findAll() here
}
}
And then in your controller, when you call $em->getRepository('My\User') it will return the custom repository you just created.

Related

Override Persister:loadAll in EntityRepository

I have the need to display data from a table in a pager and at the same time get a count on records in a child table. There are too many child records to load into memory and count, so I want to modify how the query is built in my EntityRepository.
Ideally I don't want to re-implement the pager functionality, so I am looking to override findBy in my EntityRepository and add the count(), join and group by in my query?
How do I do this best? I'm using Symfony 2.8, Doctrine 2 and PagerFanta
I also found this http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/filters.html but it seems thin on documentation
I hope I can help you!
If I understand correctly, you want to load from the database for each instance of an object the number of child objects in addition to all the fields of this object - as an additional field. Right?
I don't know, how the parent entity and the child entity are named in your model. But I will give a working example for your tasks from my web application. You have only to rename the parent and child entity.
There are affiliate programs, each of which has a finite set of accounts in my application. So, I have a parent entity called 'AffiliateProgram' and I have a child entity called 'AffiliateProgramAccount'.
You should not override the standard method in the repository for class of your entity. You just have to add your method according to your needs.
First thing, create a repository for class of your parent entity (How to Create custom Repository Classes). I do it in the YAML file with a description as follows:
Analytics\TrafficStatisticsBundle\Entity\AffiliateProgram:
type: entity
table: affiliate_program
repositoryClass: Analytics\TrafficStatisticsBundle\Entity\AffiliateProgramRepository
Then you must create a repository class of your parent entity according to the path in the description for Doctrine. I keep the classes of repositories together with the model classes in the 'Entity' directory. Now you can create your custom methods in the created repository according to your individual needs.
I suggest to use Doctrine DBAL to solve your problem (How to use Doctrine DBAL). Here is an example of my query, absolutely identical to yours:
use Doctrine\ORM\EntityRepository;
class AffiliateProgramRepository extends EntityRepository {
/**
* #return array
* #throws \Doctrine\DBAL\DBALException
*/
public function findAllAffiliatePrograms() {
// $stmt = $this->getEntityManager()
// ->getConnection()
// ->prepare('
// SELECT COUNT(apa.id) AS Number_of_accounts, ap.*
// FROM affiliate_program AS ap
// LEFT JOIN affiliate_program_account AS apa ON apa.affiliate_program_id = ap.id
// GROUP BY ap.id');
// $stmt->execute();
// return $stmt->fetchAll();
$stmt = $this->getEntityManager()
->getConnection()
->prepare('
SELECT COUNT(apa.id) AS Number_of_accounts, ap.*
FROM affiliate_program AS ap,
affiliate_program_account AS apa
WHERE apa.affiliate_program_id = ap.id
GROUP BY ap.id');
$stmt->execute();
return $stmt->fetchAll();
}
}
Here notice that I don't use object names ('AnalyticsTrafficStatisticsBundle:AffiliateProgram' in my case) for operators FROM, [LEFT | RIGHT | INNER | OUTER] JOIN as it must be used in DQL, etc. Instead, I use the real table names.
Note: The query without using the JOIN operator executes faster. In my example I showed two ways - using the JOIN operator and the same with the using of WHERE operator. Proof:
and
Now you can get all the objects according to your query in the controller, simply by calling the newly created method:
<?php
namespace Testing\TestBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
/**
* TestController
*
* #Route("/test")
*/
class TestController extends Controller {
/**
* #Route("/", name="test_index")
* #Method("GET")
*/
public function indexAction() {
$em = $this->getDoctrine()->getManager();
$affiliatePrograms = $em->getRepository('AnalyticsTrafficStatisticsBundle:AffiliateProgram')->findAllAffiliatePrograms();
return $this->render('TestingTestBundle:Test:test.html.twig', [
'result' => $affiliatePrograms
]);
}
}
And to make sure that everything works, you just write the following snippet in .twig file (for example):
{{ dump(result) }}
Materials also, see here:
How to use Raw SQL Queries in Symfony 2
Raw SQL Queries
Executing SQL directly in Symfony2 Doctrine
I hope that's all you need!

Is it considered a bad practice to add fields to Symfony entity in controller?

Is it considered a bad practice to add fields to Symfony entity in controller? For example lets say that I have a simple entity:
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
}
And then in UserController.php I want to do the following:
foreach($users as $user){
$user->postsCount = someMethodThatWillCountPosts();
}
So later that postsCount can be displayed in Twig. Is it a bad practice?
Edit:
It's important to count posts on side of mysql database, there will be more than 50.000 elements to count for each user.
Edit2:
Please take a note that this questions is not about some particular problem but rather about good and bad practices in object oriented programming in Symfony.
As #Rooneyl explained that if you have relation between user and post then you can get count easily in your controller, refer this for the same. But if you are looking to constructing and using more complex queries from inside a controller. In order to isolate, reuse and test these queries, it's a good practice to create a custom repository class for your entity.Methods containing your query logic can then be stored in this class.
To do this, add the repository class name to your entity's mapping definition:
// src/AppBundle/Entity/Product.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository")
*/
class Product
{
//...
}
Doctrine can generate empty repository classes for all the entities in your application via the same command used earlier to generate the missing getter and setter methods:
$ php bin/console doctrine:generate:entities AppBundle
If you opt to create the repository classes yourself, they must extend
Doctrine\ORM\EntityRepository.
More Deatils
Updated Answer
In many cases associations between entities can get pretty large. Even in a simple scenario like a blog. where posts can be commented, you always have to assume that a post draws hundreds of comments. In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This can lead to pretty serious performance problems, if your associations contain several hundreds or thousands of entities.
With Doctrine 2.1 a feature called Extra Lazy is introduced for associations. Associations are marked as Lazy by default, which means the whole collection object for an association is populated the first time its accessed. If you mark an association as extra lazy the following methods on collections can be called without triggering a full load of the collection: SOURCE
"rather about good and bad practices in object oriented programming"
If that's the case then you really shouldn't have any business logic in controller, you should move this to services.
So if you need to do something with entities before passing them to twig template you might want to do that in specific service or have a custom repository class that does that (maybe using some other service class) before returning the results.
i.e. then your controller's action could look more like that:
public function someAction()
{
//using custom repository
$users = $this->usersRepo->getWithPostCount()
//or using some other service
//$users = $this->usersFormatter->getWithPostCount(x)
return $this->render('SomeBundle:Default:index.html.twig', [
users => $users
]);
}
It's really up to you how you're going to do it, the main point to take here is that best practices rather discourage from having any biz logic in controller. Just imagine you'll need to do the same thing in another controller, or yet some other service. If you don't encapsulate it in it's own service then you'll need to write it every single time.
btw. have a read there:
http://symfony.com/doc/current/best_practices/index.html

Laravel Eloquent Relationships with Repository/Service Design Pattern

I am currently working on a web app that has been set up using the Repository/Service Layer Design Pattern, i.e. I have service layer that does any necessary business logic before running any methods within the repository. I have facades for each one of my models which access their respective service layers, and this has been fine for the most part. However, now that I am trying to set up Eloquent relationships, the facades seem to be causing a massive headache as I am not sure which direction I should be going.
Take the following code:
class Account extends Eloquent {
// Our table name
protected $table = "accounts";
// Our primary key
protected $primaryKey = "id";
/**
* Role Relationship
*
* Returns a list of roles associated with
* this account
*/
public function roles() {
return $this->hasMany('Role');
}
}
This will not work as is, because instead of using the entity class of Role, it is using the Role Facade. I have figured out a workaround for this, by setting an alias for the Entity with a slightly different name, such as RoleEntity so that
public function roles() {
return $this->hasMany('RoleEntity');
}
will work, however this doesn't seem like the most optimal solution.
My question is, is the practice ok? Or better yet, should this be happening at all? And if not, how do I fix it/where did I go wrong?
You have two classes with the same name in the same namespace. Use different namespaces so you can use the same class names.
I usually use \Models to locate my models classes.
At the top of each model file:
namespace Models;
In your controller or any part of your app:
\Models\Role::first();
Note that changing the namespace on your model will require you to add the namespaces of other classes i.e. Str, Eloquent, Url, Redirect, etc.
use Eloquent;
use URL;
In your model, you also have to pass the namespaces in the relationship functions, i.e.:
public function roles() {
return $this->hasMany('\Models\Role');
}

Doctrine, MVC, Symfony: where may I use Doctrine? May I use it in controller?

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.

CakePHP: using models in different controllers

I have a controller/model for projects. so this controls the projects model, etc, etc. I have a homepage which is being controlled by the pages_controller. I want to show a list of projects on the homepage. Is it as easy as doing:
function index() {
$this->set('projects', $this->Project->find('all'));
}
I'm guessing not as I'm getting:
Undefined property: PagesController::$Project
Can someone steer me in the right direction please,
Jonesy
You must load every model in the controller class by variable $uses, for example:
var $uses = array('Project');
or in action use method
$this->loadModel('Project');
In my opinion the proper way to do this is add a function to your current model which instantiates the other model and returns the needed data.
Here's an example which returns data from the Project model in a model called Example and calls the data in the Example controller:
Using Project Model inside Example Model:
<?php
/* Example Model */
App::uses('Project', 'Model');
class Example extends AppModel {
public function allProjects() {
$projectModel = new Project();
$projects = $projectModel->find('all');
return $projects;
}
}
Returning that data in Example Controller
// once inside your correct view function just do:
$projects = $this->Example->allProjects();
$this->set('projects', $projects);
In the Example view
<?php
// Now assuming you're in the .ctp template associated with
// your view function which used: $projects = $this->Example->allProjects();
// you should be able to access the var: $projects
// For example:
print_r($projects['Project']);
Why is this "better" practice than loading both models into your controller? Well, the Project model is inherited by the Example model, so Project data now becomes part of the Example model scope. (What this means on the database side of things is the 2 tables are joined using SQL JOIN clauses).
Or as the manual says:
One of the most powerful features of CakePHP is the ability to link relational mapping provided by the model. In CakePHP, the links between models are handled through associations.
Defining relations between different objects in your application should be a natural process. For example: in a recipe database, a recipe may have many reviews, reviews have a single author, and authors may have many recipes. Defining the way these relations work allows you to access your data in an intuitive and powerful way. (source)
For me it's more reasonable to use requestAction. This way the logic is wrapped in the controller.
In example:
//in your controller Projects:
class ProjectsController extends AppController {
function dashboard(){
$this->set('projects', $this->Project->find('all'));
}
$this->render('dashboard');
}
Bear in mind that you need to create dashboard.ctp in /app/views/projects of course.
In the Page's dashboard view (probably /app/views/pages/dashboard.ctp) add:
echo $this->requestAction(array('controller'=>'projects', 'action'=>'dashboard'));
This way the logic will remain in the project's controller. Of course you can request /projects/index, but the handling of the pagination will be more complicated.
more about requestAction(). but bear in mind that you need to use it carefully. It could slow down your application.

Categories