0

I just started learning Symfony2 (and I do not have much php experience), so my question may seem funny for someone. I'm now following Databases and Doctrine section of The Book, and my question concerns Fetching Related Objects example (I use the same code as in documentation, so I won't paste all of it here).

There is a code that fetches associated objects in this example:

public function showAction($id)
{
    $product = $this->getDoctrine()->getRepository('AcmeStoreBundle:Product')->find($id);
    $categoryName = $product->getCategory()->getName();

    return array('product' => $product, 'category' => $categoryName);
}

When I run this controller on a Product object that has category reference set in DB everything works fine. Unfortunately, when category is null it throws "FatalErrorException: Error: Call to a member function getName() on a non-object".

I understand that this is because there is no Category object and so no Category name, but my question is what is the best way to handle such situations? I want $categoryName to return null or empty string to use in my template, just like any other not set property of Product object, but since it comes from associated object I'm stuck on this problem

etomilin
  • 3
  • 2

2 Answers2

1

You might be expecting: When $product->getCategory() is called if $product has no Category, it returns empty Category.

If so, let's code so.

Constructor should be:

class Product
{
    /**
     * @var Category $category
     */
    private $category; //clearly mentions that Product has ONE category

    public function __construct()
    {
        $this->category = new Category();
        //initialize this so $this->category is no longer a non-object
    }
}

EDIT:

You're still get same error because that doctrine just synchronizes records into class-map like

$product = new Product();
$product->setName();
...
$product->setCategory(null); // product has no category, so set null
...

I'm sorry I might have told you a lie.

While doctrine does like above, two suggestions for solution:

  1. modify getter

    Other expression for "if the product has no category, returns empty Category instead"

    public function getCategory()
    {
        if (null !== $this->category) {
            return $this->category;
        }
    
        return new Category();
    }
    
  2. move such a "ifnull-then" logic into twig (as others say)

    in controller:

    public function showAction($id)
    {
        $product = $this->getDoctrine()->getRepository('AcmeStoreBundle:Product')->find($id);
        //$categoryName = $product->getCategory()->getName();
    
        return array('product' => $product);
    }
    

    in template:

    ...
    {{ product.category.name|default('no category') }}
    ...
    

    default('no category') does the trick. It returns 'no category' instead when:

    • when a error occurs internally ("get property from non-object" as you're facing)
    • if $product->getCategory()->getName() is falsy(empty string, null, 0, etc.)

for me, I often prefer 2 over 1 because it's done with less code.

0

Did you initialize category property within the __construct?

public function __construct(){
    ....
    $this->category = new ArrayCollection();
    ....
}
Jovan Perovic
  • 19,846
  • 5
  • 44
  • 85
  • Initially, I only initialized `products` property in `category` constructor. Now I tried to do the same with `category` in `product`, but result is the same. By the way, is it really needed? Category is ManyToOne relation for Product, so why do I need ArrayCollection? – etomilin Nov 18 '13 at 10:21
  • You're right, my mistake. If that is `ManyToOne` this is not needed. – Jovan Perovic Nov 18 '13 at 12:18