Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exclude an entity result within a controller

Tags:

php

symfony

This is my environment :

I have an entity :

Object
|____ id (integer)
|____ foo (string)
|____ configurations (collection)    

The property configurations is a collection of Configuration entities mapped with a oneToMany. Anyway, that looks like this :

Configuration
|____ id (integer)
|____ bar (string)
|____ name (string, nullable)

I have a service to get a single object :

  • This service is in a bundle which manage objects of many projects.

  • This service is not aware of which project call it.

  • This service is a set of filters, events listeners and sql queries. (something about 2K lines)

  • Consider I can't / don't want to update its get process for what I want to do here.

I have the single action that I want to act on in my controller like this :

$objMgr = $this->get('objectManager'); //get my service I talked before
$object = $objMgr->findOneById(); //get my object

return array('object' => $object);// render template

I have lots of themes managing the end user screen with a twig file in each :

theme1
|____ object_details.html.twig
theme2
|____ object_details.html.twig
themeX
|____ object_details.html.twig

These object_details.html.twig have this code to print the configurations.

{% for config in object.configurations %}
    {# div config.blablabla etc. #}
{% endfor %}

My question :

According to the context above, can I exclude all configurations with a name property not null ?

Answer requirements :

  • The answer doesn't edit the twig files (I know how to do that ^^)

  • I want to be sure that the exclusion of a configuration, will not be detected by doctrine and it deletes this config of my database on a random flush somewhere in the code.

  • The answer can be : "Dude, we can't do that". In fact, I'm very curious to know if it's possible. I have no idea at the moment.

  • The answer must respect some best practices :)

Comments anticipation : Why not edit twig files ? : I have lots of themes, I want to know if it's possible to do it once.

Why not exclude results in your db query ? : Because I want these configurations in my other pages and projects using this getter.

+50 for the best try :)

like image 777
Julien Bourdic Avatar asked Dec 03 '15 18:12

Julien Bourdic


3 Answers

What you want is filtering by criteria:

http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html#filtering-collections

And an example:

// Entity class
class Department
{
    public function getEmployeesByEmployeeTypeId($employeeTypeId)
    {
        $criteria = Criteria::create();
        $criteria->where(Criteria::expr()->eq('employeeType', $employeeTypeId));

        return $this->employees->matching($criteria);
    }
}

in your case criteria would be like

$criteria->where("name is not null");      
return $this->configurations->matching($criteria);
like image 73
Bartłomiej Wach Avatar answered Oct 10 '22 06:10

Bartłomiej Wach


building on my comments above:

One option, that would not reflect on anything but the view, would be to implement a (configurable) decorator that exposes the same api as the original object.

This way the view, or maybe even other consumers wouldn't even know that they are dealing with a special case.

For example:

<?php
namespace App\Something;

use Doctrine\Common\Collections\Criteria;

class ObjectDecorator /* implement ObjectInterface // if needed */
{
    public function __construct(Object $object)
    {
        $this->base = $object;
    }

    public function getConfigurations()
    {
        //Using Criteria to make it cleaner than a loop.
        $criteria = Criteria::create();
        $criteria->where(Criteria::expr()->isNull('name'));

        return $this->base->getConfigurations()->matching($criteria);
    }

    /*
    Override all your decorated entity getters with 
    public function getFoo() 
    {
        return $this->base->getFoo();
    }
    */
}

and in your controller use:

return array('object' => new ObjectDecorator($object));

You could extend this a number of ways:

  • extract creation/configuration to a decorator-factory-service.
  • create an interface that both objects (concrete and decorated) follow, so that they can savely be passed around.
like image 26
Yoshi Avatar answered Oct 10 '22 05:10

Yoshi


I do not really understand the whole problem, but I understand you want to get some items from a list in an entity and do not want to remove from the object for some reason. So, create a new function directly in the entity, in this function you can filter the list, and then return the new collection. In that way you do not compromises the object.

/**
 * Object
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Object
{
  /**
   * @var Collection
   *
   * @ORM\OneToMany(targetEntity="Configuration", mappedBy="object")           
   */
   private $configurations
   //...
   public function getConfigurationsWithName()
    {
        $subResult = new ArrayCollection();
        foreach ($this->configurations as $configuration) {
          if ($configuration->getName() != null) {
              $subResult.add($configuration);
          } 
        }
        return $subResult;
    }
   //...
}

Then in twig

{% for filteredObject in object.configurationsWithName %}
...
{% endfor %}

So, the framework look for any getConfigurationsWithName inside the entity and find the new function. I hope this is what you need.

like image 42
abdiel Avatar answered Oct 10 '22 06:10

abdiel