Product and Image are two entities linked by a oneToMany association (one product has many images). I try to enumerate with TWIG each product with the first image (filename fied) like this :
class ProductRepository extends EntityRepository
{
public function getProductsWithImages() {
$query = $this->createQueryBuilder('e')
->leftJoin('e.images', 'i', 'with', 'i.order = :order')
->setParameter('order' , 0)
->select('e')
->addSelect('i');
return $query->getQuery()->getResult();
}
}
But I got this error :
Method "filename" for object "\entity\product" does not exist.
I understand why (product entity has no image field). What is the best pratice to get only one child element without add a reference on the parent (like a mainImage field) ?
As the doctrine documentation explains :
A one-to-many association has to be bidirectional, unless you are
using an additional join-table. This is necessary, because of the
foreign key in a one-to-many association being defined on the “many”
side. Doctrine needs a many-to-one association that defines the
mapping of this foreign key.
I have this kind of relation in my current project and I simply defined bidirectional one-to-many association. So in your twig view you should be able to do for example :
{# first Image linked to the Product #}
{{ product.images.first }}
The attribute images is an ArrayCollection.
Hope it helps
Related
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();
in my Symfony2 project, I've two entities: Spot and Weather, with a one-to-many relationship "weatherReports" between the two entities.
Some weather reports are outdated, so I would like do create an "activeWeatherRecords" method to filter the Weather entities in a Spot entity.
Unfortunately, I can't see how to do this. The idea is not to fetch objects from the controller since the Spot objects are favorited, linked to an User object and accessed directly from a twig template.
So here is the question: What's the best way to filter a relationship directly from a twig template?
UPDATE 09/11/2013
I managed to filter my relationship with a filtering method on my relationship.
In my spot entity, I declared a getActiveWeatherRecords() method:
public function getActiveWeatherReports()
{
// Date
$date = new \DateTime(date('Y-m-d 12:00:00', time()));
// Criteria
$criteria = Criteria::create()
->where(Criteria::expr()->gte("date", $date))
->orderBy(array("date" => Criteria::ASC))
;
return $this->weatherReports->matching($criteria);
}
And I can call this method from a twig template as simply as follow:
[...]
{% for weatherReport in spot.activeWeatherReports %}
[...]
{% endfor %}
[...]
One way is to create a finder method that fetches only active records. You could put that method in a Doctrine repository, call it from your controller (or the Service Layer) and pass it to your template.
Another way is to add a filtering method right to your entity. This way you don't have to call a separate method and pass the result to a template — the entity you pass to the entity will be enough.
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];
}
I have an entity Image which has the property "categories". Categories is an instance of Doctrine's ArrayCollection and holds different Category objects.
I now want to fetch all Image objects containing the Category object "main".
With normal properties this looks like this:
$repository->findBy(array('category' => 'main'));
Is this also possible with Array properties or do I have to fetch the Images over the Category side?
Regards!
The ->findBy() method only works on the owning side of relations.
You can also create a custom method in your repostiory:
public function findByCategoryName($categoryName)
{
return $this
->createQueryBuilder('image')
->innerJoin('image.categories', 'category')
->where('category.name = :categoryName')
->setParameter('categoryName', $categoryName)
->getQuery()
->getResult()
;
}
I'm using Symfony2 / Doctrine2.
I'm trying to override the BaseEntityRepository Class so the magic findBy method automatically adds a JOIN on a "translations" relation. It was easy to do in Symfony 1.4/Doctrine 1, because it was manipulating a Doctrine_Query object, so I simply had to $query->addJoin() and it did the trick.
Unfortunately, in Doctrine 2 you only receive an array of criteria as parameter, and that's where I'm stucked.
I got many entities which have a one-to-many relationship with a translation entity.
For exemple : Section as a one-to-many relationship with SectionTranslation
The goal is to retrieve only the "active" sections (active is in SectionTranslation) when using SectionRepository->findAll(); (or even findBy).
The wanted DQL result is : Select * from Section INNER JOIN SectionTranslation ON Section.id = SectionTranslation.translatable_id WHERE SectionTranslation.locale = $locale AND SectionTranslation.active = 1;
Any idea?
The solution could be adding event listener for particular entity and particular method.