since I'm quite new to Symfony and Doctrine I got a maybe stupid question ;-)
Can someone use simple words to explain Collections (especially ArrayCollections in entities) to me? What is it and when and how to use them? (Maybe in an simple example)
Couldn't figure it out quite well in the docs...
Thanks in advance.
So the ArrayCollection is a simple class that implements Countable, IteratorAggregate, ArrayAccess SPL interfaces, and the interface Selectable made by Benjamin Eberlei.
Not much information there if you are not familliar with SPL interfaces, but ArrayCollection - permit you to save the object instances in an array like form but in an OOP way. The benefit of using the ArrayCollection, instead of standard array is that this will save you a lot of time and work, when you will need simple methods like count, set, unset iterate to a certain object, and most of all very important:
Symfony2 uses ArrayCollection in his core and is doing a lot of things for you if you configure it well:
will generate the mapping for your relationships "one-to-one, many-to-one ... etc"
will bind the data for you when you create embedded forms
When to use it:
Usually it is used for the object relationship mapping, when using doctrine, it is recommended to just add annotations for your properties and then after the command doctrine:generate:entity the setters and getters will be created, and for relationships like one-to-many|many-to-many in the constructor class will be instantiated the ArrayCollection class instead of just a simple array
public function __construct()
{
$this->orders = new ArrayCollection();
}
An example of use:
public function indexAction()
{
$em = $this->getDoctrine();
$client = $em->getRepository('AcmeCustomerBundle:Customer')
->find($this->getUser());
// When you will need to lazy load all the orders for your
// customer that is an one-to-many relationship in the database
// you use it:
$orders = $client->getOrders(); //getOrders is an ArrayCollection
}
Actually you are not using it directly but you use it when you configure your models when setting setters and getters.
Related
I am trying to implement a time tracking mechanism in my custom project management app.
This app contains multiple entities (tickets, projects, wiki pages, sprints, ...)
I want my timetracking to be "generic" in the sense that I want users to be able to log time against a ticket, project, wiki page, ...well any entity actually.
Now, I am trying to figure out what database schema (relation) to use for my TimeLog entity.
I could theoretically create a relation to each entity I have in my app, but that will require me to keep updating schema when I introduce new entities later on.
Has anybody every implemented anything like this?
All suggestions are welcomed.
Many thanks in advance.
I faced a similar situation in my app while trying to add comments, likes and other types of elements whose behaviour would not really depend on the entity they are attached to.
The solution I eventually chose was to have two fields in my referring entities (e.g. Comment) to hold both the id of the entity being referred to and its type. Since I was using this multiple times, I put the properties into the following trait:
namespace AppBundle\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
trait EntityReferenceTrait
{
/**
* #ORM\Column(name="reference_id", type="integer")
*/
private $referenceId;
/**
* #ORM\Column(name="reference_type", type="integer")
*/
private $referenceType;
/* ... setters & getters ... */
}
Then I could use it in the entities holding those kind of references:
/**
* #ORM\Table(name="comments", indexes={#ORM\Index(name="references", columns={"reference_id", "reference_type"})})
* #ORM\Entity(repositoryClass="AppBundle\Repository\Comment\CommentRepository")
*/
class Comment
{
/* ... other traits ... */
use \AppBundle\Entity\Traits\EntityReferenceTrait;
/* ... other fields & methods ... */
}
Note: I added an index for the references but it is not necessary for the whole thing to work properly. If you use such an index, beware of the order of your WHERE clauses if you want to benefit from it
In order to improve performance a bit and add additional configurations depending on the type of the entity being referred to, I handled settings directly in the config of my app. Thus, I have something like:
commentables:
news:
classname: AppBundle\Entity\News\News
type_id: 1
browse_route: news_comments
multiple_locales: false
...
This allows me to know precisely what kind of entities my Comment entity can refer. It also allows me to automatically hook specific listeners to the entities being referred to so that the removal of a referred entity triggers the removal of the related comments for example. I do this by processing the configuration in AppBundle/DependencyInjection/AppExtension.php (more about this here) and saving the needed listeners list into a parameter. Then, by adding a listener to the loadClassMetadata event, I can effectively handle the removal of related entities for example.
Here is the listener that hooks the listeners for specific lifecycle events of referred entities by using addEntityListener on the ClassMetadata instance:
namespace AppBundle\Listener;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
class MappingListener
{
private $entityListenersMapping = [];
/**
* #param array $mappingConfig Associative array with keys being listeners classnames and values being arrays associating an event to a method name
*/
public function __construct(array $mappingConfig)
{
$this->entityListenersMapping = $mappingConfig;
}
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
if(!array_key_exists($classMetadata->name, $this->entityListenersMapping))
{
return;
}
// Hook the entity listeners in the class metadata
foreach($this->entityListenersMapping[$classMetadata->name] as $listenerClassName => $eventsCallbacks)
{
foreach($eventsCallbacks as $event => $methodName)
{
$classMetadata->addEntityListener($event, $listenerClassName, $methodName);
}
}
}
}
Either way, for this part, it mainly depends on the specific needs of your entity but I guess it is quite a common need that these "soft" foreign keys emulate a ON DELETE CASCADE behaviour via preRemove and postRemove events.
Considering the handling of those references and the entities owning them, I also created a EntityRefererManagerTrait to easily create services that manage those entities so that the other components interacting with them would not have to worry about the underlying configuration.
The interface of most public methods of those managers thus usually require:
the classname of the entity being referred to
the numeric id of the entity being referred to
With those two info and the configuration retrieved in my manager service, I can easily interact with the database even if, in my case, it stores an integer defined in the configuration as the reference type in place of the classname of the entity being referred to.
Based on this, I can enable comments, likes, votes, subscriptions and so on for any of my app entities (as long as its primary key is a single integer) with just a few more lines in my configuration files. No need to update database schema and with proper lifecycle events listeners being hooked, no worries about orphan entries in the database.
On a side note, it should be mentioned that you won't be able to retrieve referring entities from the inverse side as it won't be a real association. You won't benefit from foreign keys behaviours either. Thus, even if you emulate the ON DELETE CASCADE behaviour by listening to remove events, you won't be able to ensure that there are no orphans in your database if some DELETE operations are performed directly via DQL for example.
Can anyone tell me how to implement Doctrine NotifyPropertyChanged in Symfony?
I have implemented this code in an Entity that will notify about is changes. But how to add listener for these changes in another Entity?
I mean I want to handle this situation: Entity Book has oneToMany to Authors. When something is being changed in a partcular Author, I want to react on it in a Book Entity. So I have implemented NotifyPropertyChanged in Author. In a setter I invoke notifications, but how to add this listener to a Book entity?
It looks like your use case is a little bit different: the ǸotifyPropertyChanged` is usefull to tell Doctrine your entity changed. By default, Doctrine2 will iterate over all properties and compare them (so that's "automatic"). IMO, drop it. Only use case I see for it right now are some very specific cases were using the default policy would cost too much.
Another way to do would be to use some lifecycle events. Changing other entities from a preUpdate or postUpdate is notoriously difficult. Doctrine already started calculting the "changeset", what to persist to DB, and there's no easy way to add another entity to it.
Since you are ok writing some code inside the setters, the easier path is something like this:
class Author
{
public function setName($name)
{
// ...
// for each setter, call onChanged
$this->onChange();
}
private function onChange()
{
foreach ($this->books as $book) {
// maybe call some method on your books, like onAuthorChanged() ?
}
}
}
I'm curious if there's a standard/recommended practice for the name of the variable storing the entity in a controller action or view.
public function demoAction(User $entity)
{
return $this->render('...', [
'entity' => $entity,
]);
}
I've seen some places where it's done this way (always name the main entity var $entity) and other's where $entity is $user (for example).
In my opinion, using $entity everywhere get's confusing when there are 2 entities and then you either have one called "entity" and the other something else.
Is there a standard/recommended practice? For Symfony? Or maybe something for the wider PHP world? Or for an ORM, ie, Doctrine?
Yeah, call them what they are, like $user, $userProfile ...
The $entity is from autogenerated stuff Symfony creates. Maybe this behavior could be optimized in Symfony3, so that the generated variable names are derived from the concrete class name.
Unlike the methods naming conventions (e.g. yourmethodAction in controllers which the routing process depends on), you can name your variables using your own appreciation.
Of course, as in all other programming languages and frameworks, choose logical and comprehensible names.
For entities, I generally use the name of the entity, in camelcase.
i.e. For an entity named ProductCategory, you can use $productCategory
See the Symfony coding standards for more informations.
I would like to return the page Entity from the method: getByPath($path). I just would like to know where this method should be in the script. Inside the controller or inside the entity class?
In my opinion the entity "Page" shouldn't have a function called "getByPath()" since an entity should only contain database information of one entity, which can be get or set by getters and setters. And this "getByPath" function is not just a getter or setter it requires me to run the entitymanager within the entity. Am I right?
So am I right that I should make a PageController and create the "getByPath()" (which will return the page object) function there? Or would anyone create that function inside the entity class?
I would like to know what the nicest way is to accomplish this.
Thanks in advance.
You should put that function inside a custom repository for the Page entity
While the Entities are the objects you are storing, the Repository is the class that provides methods to access/load those objects, eg when you call $em->getRepository('Entities\Page')->find($page_id);, you call the find() method on your Page repository and it's its job to find it for you.
Doctrine provides a default repository for each entity (with the various find*() methods, ...), but you can provide a custom one where you can add your own method, such as getByPath().
Symfony 2 - Database and Doctrine - Custom Repository Classes
Doctrine 2 - Custom repository
I would like to know if it is possible to avoid using getters and setters for a Symfony 2 entity. Although the php app/console doctrine:generate:entities is very helpful, line codes seem to increase a lot for an entity with a lot of fields
You can define the visibility of your properties from protected or private to public so you now have access to them in this way:
// on entity
public $someProp;
// On your code
$someEntity->someProp = someValue;
This is not a good OOP practice and should be avoided, getters and setters are the way to go if you want clean and secure code.
You Code won't work properly if skip getters and setters.
Especially if you have mapping with different Entities.
Doctrine uses getters nad setters to populate various members fields during storing and fetching of information
Its very bad practice to use public for every property in Entity.
and also you will end up wasting lot of time in changing the property to public as doctinr uses proted when it autogenerate the code