Symfony - Read-only entities hydrated from YAML - symfony

Is it possible to have simple read-only entities, that can have an association with an other doctrine entity, but their data is stored in a text ( YAML ) file ?
Let's say I have a product entity, and I want to set a category for each product. But for now, I only have very few categories ( and don't need to edit or add ), so I don't want/need to create a full doctrine entity with it's own table in the DB.
So I create a very simple entity:
class ProductCategory
{
private $id;
private $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
}
Now I would like to create a simple YAML file where the data is stored, like so:
0: Cheese
1: Meat
2: Dairy Products
....
Then I would like to set a ManyToOne relation from the product entity to the ProductCategory entity.
Is that possible ?
And, how to "query" the categories ? ( let's say I want to list all categories that start with a certain letter )
'Why' you ask ?
Well for now, as I said, I only have very few categories, but maybe some day I want to add many more, and even have a CRUD editor for them and so on, then I could easily convert it to a full doctrine entity.
Or any other suggestions on how to approach this ?

There is already a library that provides what you are looking for that's called Alice:
https://github.com/nelmio/alice
https://github.com/hautelook/AliceBundle
https://github.com/fzaninotto/Faker
https://github.com/h4cc/AliceFixturesBundle
This way you can create random test data en masse and can still work with Doctrine as usual.
If you want to do this manually it will be a pain to solve the problem of connecting the entities. Your best bet is to keep all of them in arrays with id's being used as keys but even then you will probably end up writing lots of glue code to connect the entities.

Related

Set up non-persistent relation in Doctrine 2

I have an object $user that has a one to many relation with $establishment. I can use:
$user->getEstablishments();
The user can select a stablishment to work on. I have this method that I call in the controller:
$user->setCurrentEstablishment($establishment);
And this one that I call in the view:
$establishment = $user->getCurrentEstablishment();
I want to be able to call:
$user->setCurrentEstablishmentBy Slug($establishment_slug);
where the slug is a string, and let the user object look for the establishment.
Doctrine discourages the practice of accessing the Entity Manager inside the Entity object, but I think that using it in the controller is even worse.
I suspect that some special Doctrine annotation exists that takes care of non persistent relations like this, or some method other than serving the Entity Manager through a service should be used here. Some easy way of referencing other entities from inside the model.
¿Is there any? ¿How could I do that?
There is no Annotation in Doctrine which could convert slug into object.
What can help You is ParamConverter, with it you can automatically convert slug from query into object. But it still must be used in Controller.
Example usage:
/**
* #Route("/some-route/{slug}")
* #ParamConverter("object", class="AppBundle:Establishment", options={"id" = "slug", "repository_method" = "findEstablishmentBySlug"})
*/
public function slugAction(Establishment $object)
{
...
Docs about param converter: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html

Doctrine ManyToMany, find related and no related results

I have an entity called tournament to which users can register to participate in.
The relationship between the two entities is ManyToMany and need to create a view of Symfony2 in which list all tournaments, with or without registered users so that they can join.
This is my DoctrineQueryBuilder
$em->createQueryBuilder('d')
->select('d, i, u')
->leftJoin('d.item','i')
->leftJoin('d.users','u')
->where('d.active = 1')
->andWhere('d.state = 1')
->orderBy('d.dateStart', 'ASC');
I also need to get the number of users who have joined the tournament.
Preamble
There are various ways to achieve what you want. You can create a sub-query to do the count, however a simpler solution is to let doctrine handle this for you.
The solution described below is based on Doctrine lazy/eager loading capability. When doctrine loads an entity, it will also populate it's associations, either lazily or eagerly (default is lazy).
Solution
Assuming your Tournament entity maps the users association as a ManyToMany relation. You can create a new method which counts your Tournament->users.
ManyToMany associations would populate the entity property (in this cases $users) with an ArrayCollection.
A method called countUsers would do the trick, example implementation below:
...
class Tournament {
...
/**
* #ManyToMany(targetEntity="User")
* #JoinTable(name="tournament_users",
* joinColumns={#JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="tournament_id", referencedColumnName="id"}
* )
**/
private $users;
...
public function countUsers(){
return $this->users->count();
}
In the view when iterating over the collection of tournaments, simply call the $tournament->countUsers() method to display the count.
References:
ArrayCollection: http://www.doctrine-project.org/api/common/2.1/class-Doctrine.Common.Collections.ArrayCollection.html

How do I setup up Doctrine's Class Inheritance in Symfony?

My issue is, I'm having trouble grasping DiscriminatorColumn and DiscriminatorMap in Doctrine's Class Inheritance.
I have a products entity that is considered the parent class / table.
There are several child entities that inherit the product entity. (models, parts, and options)
I feel like I should be able to use the primary key to link both tables... But how do I do that with DiscriminatorColumn?
Here is the general idea of what I want to happen...
Fetch all model objects from database while inheriting product parent entity
SELECT object
FROM parts_object parts
LEFT JOIN products_object po
ON parts.product_fk = po.product_id
Or... Fetch all part objects from database while inheriting product parent entity
SELECT object
FROM parts_object parts
LEFT JOIN products_object po
ON parts.product_fk = po.product_id
Ideally I want this done using Doctrine instead of some custom SQL.
Do I need to setup a "type" column for the parent table so each row defines whether it's a part, model, or option?
Doctrine inheritance docs
Okay, I'll try to explain this as simple as possible.
Let's start with DiscriminatorColumn
Discriminator column is basically, as it says, a column in your database. Its used to store, a key, if you like which helps to identify what kind of object you're currently querying, based on your DiscriminatorMap configuration.
DiscriminatorMap is the way you map each of those keys to an entity. You said you have the following
Product [parent]
Model [child of parent]
Part [child of parent]
Option [child of parent]
Then, your discriminator map should look something like this, for example:
#DiscriminatorMap({
"model" = "AppBundle\Entity\Model",
"Part" = "AppBundle\Entity\Part",
"Option" = "AppBundle\Entity\Option"
})
Always pay attention to your last definition in your mapping. The last line must end without a comma!
As of InheritanceType I would suggest you to use #InheritanceType("JOINED") because this will let you have single table for each of your child classes.
Every child class must extend your Product entity class, which is obviously the parent. Each child class must not define $id property, because of the inheritance mapping.
Then querying for records by specific type comes with the following query:
"SELECT product FROM AppBundle\Entity\Product product WHERE product INSTANCE OF AppBundle\Entity\Part"
The query will search only for records mapped to this entity only.
If you have any questions, don't hesitate to ask.
Edit as of new comment
-----------------------
A little bit more explanation. You do not need to create any extra property/column in your entity mappings. The moment you add this annotation #DiscriminatorColumn(name="discr", type="string") doctrine will create that column automatically for you. The column from this example would be named discr with type of VARCHAR.
I still don't understand what is used to join the tables. How does doctrine know to link the ids between the product and model
About this part. If you use #InheritanceType("JOINED") this would mean that your GeneratedValue ID would be set in your main entity - Product. Then each of the child entities that extend Product would automatically get the same ID, which is why you don't need to specify $id property in your child entities.
Lastly, how can you check which entity type you're currently viewing for example. Consider the following scenario, each of your child entities extends Product and we will perform a dummy search for a record:
$product = $entityManager->find('AppBundle:Product', 1); // example
Now, if you actually go and do a var_dump($product) you will notice something interesting. The object would be an instance of either Model,Part or Option because each of these entities are defined in your discriminator map and Doctrine automatically maps your records based on that.
Later, this can come handy in situations like this:
if( $product instanceof \AppBundle\Entity\Part ) {
// do something only if that record belongs to part.
}
If you want to use DiscriminatorMap for Doctrine, so you should use Doctrine, but not SQL.
Basic setup is:
/**
* #ORM\Table(name="product")
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\ProductRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="productType", type="string")
* #ORM\DiscriminatorMap({
* "Product" = "Product",
* "Model" = "Model",
* "Part" = "Part",
* "Option" = "Option",
* })
*/
class Product
{
...
}
MyApp\ProductBundle\Entity\Model
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\ModelRepository")
*/
class Model extends Product
{
}
MyApp\ProductBundle\Entity\Part
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\PartRepository")
*/
class Part extends Product
{
}
MyApp\ProductBundle\Entity\Option
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\OptionRepository")
*/
class Option extends Product
{
}
Then if you need to get all products at controller
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository("MyAppProductBundle:Product");
$products = $repo->findAll();
Then if you need select all models, just setup proper repository
$repo = $em->getRepository("MyAppProductBundle:Model");
$models = $repo->findAll();

Symfony2.1 Circular references

I'm wondering how to avoid having a circular reference in my symfony2.1 application.
I have an entity like
customer (
name
addresses -- OneToMany
currentAddress -- OneToOne )
and
address (
street
customer -- ManyToOne )
Now my fixtures won't load because it can't delete the customer because of the foreign key.
For performances' sake I would like to avoid having to add a getCurrentAddress() method on customer which would select in the addresses table.
Does anybody have a solution for that?
Adding a getCurrentAddress() isn't such a performance issue.
This way, I'll avoid the circular reference and all the issues that come along with it.
In my situation, using an order by date in the doctrine annotation was enough:
// on customer entity :
/** #ORM\OrderBy({"datemodified" = "DESC"}) */
private $addresses
public function getCurrentAddress()
{
return $this->addresses[0];
}

Doctrine2 Querying in entities

I'm using SF2.1 with Doctrine2
I have 2 entities : Category and Article, linked with a oneToMany relation.
I want to retrieve all articles of a specific category, but only the published ones ....
I show you what I do for the moment :
// in /Entity/category.php :
public function getAllArticlesPublished(){
$articles = array();
foreach($this->getArticles() as $article){
if($article->isPublished()) $articles[] = $article;
}
return $articles;
}
Is this the good way to do it ? It doesn't look good to parse the whole array of Articles ... but I don't want to do this in the repository becauseit concerns a specific Category.
Any hint for me ?
You might take a look at Filtering Collections. If it doesn't solve your problem, then a repository would be the best place for stuff like this.

Resources