2
votes

I am new to symfony/doctrine and when setting up my first table and query I am struggling with the best way to output results in Twig.

So, I have this method on \AppBundle\Controller\BrandsController

    public function showAction($brand)
{

   $product = $this->getDoctrine()
       ->getRepository('AppBundle:Brands')
       ->findOneByBrand($brand);

   if (!$product) {
       throw $this->createNotFoundException(
           'No product found for id '.$brand
       );
   }
    return $this->render('brands/brands.html.twig', [
        'product' => $product
    ]);
}

This produces an object like below, which I cannot iterate over.

Brands {#459 ▼
-brand_id: 24
-brand: "Ford"
-active: "Y"
-img_logo: "/img/brand/ford.png"
-img_logo_small: "/img/brand/ford_20.png"
-img_logo_big: "/img/brand/ford-big.png"
}

Of course I can create a query like below, but that negates the benefit of the findBy() method:

$repository = $this->getDoctrine()
->getRepository('AppBundle:Brands');
$query = $repository->createQueryBuilder('p')
    ->where('p.brand = :brand')
    ->setParameter('brand', $brand)
    ->getQuery();
    $product = $query->getSingleResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);

I found similar questions, like this one, but they mess up with the array keys by giving a array that looks like:

array:6 [▼
"\x00AppBundle\Entity\Brands\x00brand_id" => 24
"\x00AppBundle\Entity\Brands\x00brand" => "Ford"
"\x00AppBundle\Entity\Brands\x00active" => "Y"
"\x00AppBundle\Entity\Brands\x00img_logo" => "/img/brand/ford.png"
"\x00AppBundle\Entity\Brands\x00img_logo_small" => "/img/brand/ford_20.png"
"\x00AppBundle\Entity\Brands\x00img_logo_big" => "/img/brand/ford-big.png"
]

By the way, that's the simple version of the code on brands/brands.html.twig:

 {% for item in product %}
      <p> This is my   {{ item }}</p>
 {% endfor %}

Is there a clean way to do it?

Thanks

2
you dont need to convert it to an array, to output the object in you'r twig file , just {{product.id}} {{product.brand}} etc..famas23
@Bhs. That would be awkward, having to mention each key. This should use a for loop and iterate over the object/array like: {% for item in product %} <p> This is my {{ item }} </p> {% endfor %}BernardA
you need to structure your array from your object to iterate it ,take a look here stackoverflow.com/questions/7093515/…famas23
Also here on github ther's a serlize script : github.com/borisguery/bgylibrary/blob/master/library/Bgy/…famas23
@Bhs, the serializer script worked for me. Put this up as a proper answer and I will accept it, thanks.BernardA

2 Answers

0
votes

The idea is to use Entity Serializer Doctrine Script , look to the link below : https://github.com/borisguery/bgylibrary/blob/master/library/Bgy/Doctrine/EntitySerializer.php
Just copy the script into a new class : EntitySerializer.php , under AppBundle\Controller , then in your BrandsController class:

$em = $this->getDoctrine()->getManager(); 
$product = $em->getRepository('AppBundle:Brands') ->findOneByBrand($brand); 
$serializer = new EntitySerializer($em);
$product = $serializer->toArray($product);

and finnally you can easily iterate the array :

foreach ($array as $key => $value) {
    echo 'the key is '.$key.'<br>';
    echo 'the value is '.$value;
}
-1
votes

General... It's kinda bad idea to iterate through your product in twig like an array. Just because it's matter of time till you'll need more controll like show/do this if product is active or not... and so on. So pretty soon you'll have to do somthing like this (just an wild example of possible usecase...)

{% if product|default %}

  {# if logo isn't there or empty or similar use path to no-logo.png as fallback  #}
  {% set _product_logo_path = product.img_logo|default('/assets/images/no-logo.png') %}

  {{ product.brand }} <img src="{{- _product_logo_path  -}}" title="blah blah" />

  {% if not product.isActive|default %}
   <span>currently unavailable</span>
  {% endif %}
{% endfi %}

Architectual...

You can use your repository for that kind of "manipulation"

something like :

in your controller:

$product = $this->getDoctrine()
       ->getRepository('AppBundle:Brands')
       ->findOneForViewByBrand($brand);


// concider to use instanceof since you expecting Brands object
// if not stay with !$product
if( false === $product instanceof Brands )
{
   // not found exception..
}

in your Repo-Class

public function findOneForViewByBrand( $brand )
{

  // you ca make use of PARTIAL SELECTS
  // NOTE: if you use PARTIAL selects you have to select at least id, like PARTIAL p.{brand_id}

  $query = $this->createQueryBuilder('p')
    ->select('PARTIAL p.{brand_id, brand, active, img_logo, img_logo_small, img_logo_big}')
    ->where('p.brand = :brand')
    ->setParameter('brand', $brand)
    ->getQuery();


  // If found you'll get a Brand BUT ONLY field in curly brackets will be populated with data, others will be just NULL
  return $query->getOneOrNullResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY); 


}

and then in your twig

{% for fieldName, fieldValue in product %}
      {# should skip null values #}
      {% if fieldValue is defined and fieldValue is not empty % }
      <p> This is my   {{ fieldValue }}</p>
     {% endif %}
 {% endfor %}

About Partial Selects

P.S. code is untested and it's here so you can get an idea ;) I don't know what version of symfony you use and what version of doctrine is installed, so if one of mentioned methods doesn't exists - please don't be angry :)