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

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

This sounds like what I want, but unfortunately even when I add this to my product class, I still get 'Call to a member function getName() on a non-object'
Then, doctrine's internal must have done something you do not expected. Could you update your post to show whole code of Product and Category?
Ah, it's not needed as you say it is documented! I'll try reading them
0

Did you initialize category property within the __construct?

public function __construct(){
    ....
    $this->category = new ArrayCollection();
    ....
}

2 Comments

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?
You're right, my mistake. If that is ManyToOne this is not needed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.