SensioFrameworkExtraBundle icon indicating copy to clipboard operation
SensioFrameworkExtraBundle copied to clipboard

Problem with converter and option route parameter

Open ByScripts opened this issue 11 years ago • 4 comments

Imagine this route:

feature_product:
    pattern: /category/{category}/feature-product/{product}
    defaults:
        _controller: MyBundle:MyController:feature
        product: ~

And this action:

/**
 * @Template
 */
public function featureAction(Category $category, Product $product = null)
{
    // If a product has been selected
    if($product instanceof Product) {
        $category->setFeaturedProduct($product);
        return $this->redirect(...);
    }

    // Else, if no product has been selected, propose a list
    $featurableProducts = $category->getProducts();
    return ['products' => $featurableProducts];
}

Normally, calling URL /category/23/feature-product should set $category to a Category object with id 23, and $product should be null.

But in fact, what happens is that:

  • $category is a Category object with id 23
  • $product is a Product object with a category_id of 23

The converter makes a SELECT ... FROM product WHERE category_id = 23 LIMIT 1.

I don't think it's the way the converter should work.

ByScripts avatar Feb 06 '14 12:02 ByScripts

I'm also running into this issue when trying to use an optional parameter - is there a known workaround for this issue?

blackbourna avatar May 12 '14 17:05 blackbourna

+1

Opting into this would let me have some idea of what's going on. Took about an hour of searching around just to find the magic annotations to do this... except this is now the only thing in my project using annotations and resulted in funky strangeness.

Opting out via annotations is spooky: I never asked my code to do this thing.

Incognito avatar May 12 '14 18:05 Incognito

+1

I have this route:

/**
     * @Route("/{section_slug}/{article_id}", name="article")
     * @ParamConverter("section", class="SiteCoreBundle:Section", options={"section_slug"="slug"})
     * @ParamConverter("article", class="SiteCoreBundle:Article", options={"article_id"="id"})
     * @Template()
     */
    public function articleAction(Section $section, Article $article)
    {
        return compact('article');
    }

and when going to "http://localhost/app_dev.php/signins/5" I have result:

Unable to guess how to get a Doctrine instance from the request information.

The slug of section "signins" exists in DB and the "5" article exists too

igormancos avatar Aug 20 '14 06:08 igormancos

@ByScripts Tl;dr You can work around this by using the ParamConverter annotation for the optional parameter and explicitly listing the mapping between the route parameters and the entity property. Something like this:

@ParamConverter("product", class="YourProduClass", options={"mapping": {"product": "id"}})

The DoctrineParamConverter will first try to find the entity by id (see apply method). If this fails (as there is not id in your example) it will fall back to class repository->findOneBy() (see findOneBy). If you do not provide a mapping the DoctrineParamConverter will try mappings as parameter => parameter for all route parameters. In your example it will look for fields named product and category in your product class. If such fields happens to exist, it will then call findOneBy with this mapping. So if your product class has a property named category it will fetch a product with category 23.

I hope this made some sense, just have a look at the apply and findOneBy method of the DoctrineParamConverter.

TPete avatar Mar 31 '15 09:03 TPete