Search code examples
symfonydoctrine-ormsymfony-2.3

Get values from Media entity using Product entity and Many To Many relationship between tables


I have three entities: product, media and product_has_media which is the result of a n:m relation. This are the entities for each:

Product.php

namespace ProductBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;

/**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */
class Product {

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    protected $name;

    /**
     * @ORM\Column(type="string", length=255)
     */
    protected $description;

    /**
     * @ORM\Column(type="smallint")
     */
    protected $age_limit;

    /**
     * @Gedmo\Timestampable(on="create")
     * @ORM\Column(name="created", type="datetime")
     */
    protected $created;

    /**
     * @Gedmo\Timestampable(on="update")
     * @ORM\Column(name="modified", type="datetime")
     */
    protected $modified;

    /**
     * @ORM\ManyToMany(targetEntity="CategoryBundle\Entity\Category", inversedBy="products")
     * @ORM\JoinTable(name="product_has_category")
     */
    protected $categories;

    /**
     * @ORM\ManyToMany(targetEntity="ProductBundle\Entity\ProductDetail", inversedBy="products_details")
     * @ORM\JoinTable(name="product_detail_has_product")
     */
    protected $details;

    /**
     * @ORM\OneToMany(targetEntity="StockBundle\Entity\KStock", mappedBy="sku")
     */
    protected $stocks;

    /**
     * @ORM\OneToMany(targetEntity="ProductBundle\Entity\ProductHasMedia", mappedBy="product")
     */
    protected $medias;

    public function __construct() {
        $this->categories = new \Doctrine\Common\Collections\ArrayCollection();
        $this->details = new \Doctrine\Common\Collections\ArrayCollection();
        $this->stocks = new \Doctrine\Common\Collections\ArrayCollection();
        $this->medias = new \Doctrine\Common\Collections\ArrayCollection();
    }

    public function getId() {
        return $this->id;
    }

    public function setName($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }

    public function setDescription($description) {
        $this->description = $description;
    }

    public function getCondition() {
        return $this->condition;
    }

    public function setAgeLimit($age_limit) {
        $this->age_limit = $age_limit;
    }

    public function getAgeLimit() {
        return $this->age_limit;
    }

    public function setMedias(\MediaBundle\Entity\Media $medias) {
        $this->medias[] = $medias;
    }

    public function getMedias() {
        return $this->medias;
    }

    public function setCreated($created) {
        $this->created = $created;
    }

    public function getCreated() {
        return $this->created;
    }

    public function setModified($modified) {
        $this->modified = $modified;
    }

    public function getModified() {
        return $this->modified;
    }

}

ProductHasMedia.php

<?php

namespace ProductBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="product_has_media")
 */
class ProductHasMedia {

    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="ProductBundle\Entity\Product")
     * @ORM\JoinColumn(name="product", referencedColumnName="id")
     */
    protected $product;

    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="MediaBundle\Entity\Media")
     * @ORM\JoinColumn(name="media", referencedColumnName="id")
     */
    protected $media;

    public function setProduct(\ProductBundle\Entity\Product $product) {
        $this->product = $product;
    }

    public function getProduct() {
        return $this->product;
    }

    public function setMedia(\MediaBundle\Entity\Media $media) {
        $this->media = $media;
    }

    public function getMedia() {
        return $this->media;
    }
}

In media exists a column called url and I need to get it just having the id of the product. Can any help me to get this work? I'm lost


Solution

  • You have a couple mapping issues in your entities, if they are just mistakes then the answer to your question is relatively simple.

    First, your mapping issues

    If you are creating a bi-directional relationship (so media knows about product_has_media and vice versa) then you need to specify both the inversedBy argument and the mappedBy argument. I think you have tried to create bi-directional relationships because you already specified the mapping on the product entity.

    You can checkout the doctrine docs for this here: http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html

    At the moment you will have a warning in your debug toolbar about broken entities because you have only specified the mappedBy argument. To fix this add the inversedBy argument to the product_has_media entity:

    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="ProductBundle\Entity\Product", inversedBy="medias")
     * @ORM\JoinColumn(name="product", referencedColumnName="id")
     */
    protected $product;
    
    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="MediaBundle\Entity\Media", inversedBy="products")
     * @ORM\JoinColumn(name="media", referencedColumnName="id")
     */
    protected $media;
    

    Hopefully you already have mappings for product_has_media in your media entity, if not please post your media entity mappings so I can update my answer to help.

    Now a possible solution

    Now that you have the correct mappings you can add a method to your media repository to fetch the media entities by product id:

    public function fetchByProductId($id)
    {
        return $this->createQueryBuilder('m')
            ->leftJoin('m.products','phm')
            ->leftJoin('phm.product','p')
            ->where('p.id = :id')
            ->setParameter('id', $id)
            ->getQuery()
            ->getResult();
    }
    

    In the above function we have created a query that joins the product_has_media table using the alias "products" specified in the inversedBy mapping and the product table using the alias "product".

    This means that in your controller you can now get any media entities related to your product and just use the getUrl() method to get their url values:

    public function someAction($productId)
    {
        $medias = $this->getDoctrine()->getRepository('ProductBundle:Media')->fetchByProductId($productId);
        foreach($medias as $media){
            //do something with $media->getUrl();
        }
    }
    

    Obviously you will need to substitute values in my code with the correct values for your project.