I am trying to query a dynamic category sidebar and I am using the Sonata ClassificationBundle. I Have created a few categories(parent) and some subcategories(child). I am able to show the categories, however I cant understand how to query the subcategories under a specific category. I think i need to check if the category has children and display it? I have no idea how to do that without proper documentation..
This is the sidebar controller:
<?php
namespace Mp\ShopBundle\Controller;
use Sonata\ClassificationBundle\Entity\Category;
use Sonata\ClassificationBundle\Model\CategoryInterface;
use Sonata\Component\Currency\CurrencyDetector;
use Sonata\Component\Product\Pool;
use Sonata\ProductBundle\Entity\ProductSetManager;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class SidebarController extends Controller
{
/**
* # Route("/hello/{name}")
* # Template()
*/
public function sidebarAction()
{
$page = $this->getRequest()->get('page', 1);
$displayMax = $this->getRequest()->get('max', 9);
$displayMode = $this->getRequest()->get('mode', 'grid');
$filter = $this->getRequest()->get('filter');
$option = $this->getRequest()->get('option');
if (!in_array($displayMode, array('grid'))) { // "list" mode will be added later
throw new NotFoundHttpException(sprintf('Given display_mode "%s" doesn\'t exist.', $displayMode));
}
$category = $this->retrieveCategoryFromQueryString();
return $this->render('sidebar.html.twig', array(
'display_mode' => $displayMode,
'category' => $this->getCategoryManager()->findBy(
array('parent' => 1 )),
'subcategory' => $this->getCategoryManager()->findBy(
array('children' => true )), ////// ERROR You cannot search for the association field 'Application\Sonata\ClassificationBundle\Entity\Category#children', because it is the inverse side of an association. Find methods only work on owning side associations."
'provider' => $this->getProviderFromCategory($category),
));
}
/* $em = $this->getDoctrine()->getManager();
$products = $em->getRepository('MpShopBundle:Product')->findAll();
return $this->render('sidebar.html.twig', array(
'products'=>$products
)); */
/**
* Retrieve Category from its id and slug, if any.
*
* #return CategoryInterface|null
*/
protected function retrieveCategoryFromQueryString()
{
$categoryId = $this->getRequest()->get('category_id');
$categorySlug = $this->getRequest()->get('category_slug');
if (!$categoryId || !$categorySlug ) {
return null;
}
return $this->getCategoryManager()->findOneBy(array(
'id' => $categoryId,
'enabled' => true,
));
}
This is how I try to display the sidebar:
{% for categories in category %}
{% for subcategories in subcategory %}
<li class="subMenu"><a> {{ categories.name }} [{{ categories|length }}]</a>
<ul>
<li>{{ subcategories.name }} ({{ categories|length }})</li>
</ul>
</li>
{% endfor %}
{% endfor %}
The Category entity:
<?php
/*
* This file is part of the Sonata project.
*
* (c) Sonata Project <https://github.com/sonata-project/SonataClassificationBundle/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\ClassificationBundle\Model;
use Sonata\MediaBundle\Model\MediaInterface;
interface CategoryInterface
{
/**
* #param $name
*
* #return mixed
*/
public function setName($name);
/**
* Get name
*
* #return string $name
*/
public function getName();
/**
* Set enabled
*
* #param boolean $enabled
*/
public function setEnabled($enabled);
/**
* Get enabled
*
* #return boolean $enabled
*/
public function getEnabled();
/**
* Set slug
*
* #param integer $slug
*/
public function setSlug($slug);
/**
* Get slug
*
* #return integer $slug
*/
public function getSlug();
/**
* Set description
*
* #param string $description
*/
public function setDescription($description);
/**
* Get description
*
* #return string $description
*/
public function getDescription();
/**
* #param integer $position
*/
public function setPosition($position);
/**
* #return integer
*/
public function getPosition();
/**
* Add Children
*
* #param CategoryInterface $children
* #param boolean $nested
*/
public function addChild(CategoryInterface $children, $nested = false);
/**
* Get Children
*
* #return \Doctrine\Common\Collections\Collection $children
*/
public function getChildren();
/**
* Set children
*
* #param $children
*/
public function setChildren($children);
/**
* Return true if category has children
*
* #return boolean
*/
public function hasChildren();
/**
* Set Parent
*
* #param CategoryInterface $parent
* #param boolean $nested
*/
public function setParent(CategoryInterface $parent = null, $nested = false);
/**
* Get Parent
*
* #return CategoryInterface $parent
*/
public function getParent();
/**
* #param MediaInterface $media
*/
public function setMedia(MediaInterface $media = null);
/**
* #return MediaInterface
*/
public function getMedia();
/**
* #param ContextInterface $context
*/
public function setContext(ContextInterface $context);
/**
* #return ContextInterface
*/
public function getContext();
}
MY IDEA
Okay since all the parent categories have a parent_id of 1, i display them easily. The child categories have parent_id that is the same as the id of the parent category. Is it possible to somehow query this condition? Like parent_id = id?
You should simply use getChildren() on every parent category: the entity should be hydrated, so the query is already done for you.
Related
I am trying to filter Laravel Nova resource (Reviews) data using 2 'select-filters'.
I have a filter A = Manufacturers and filter B = Models.
A Manufacturer has many Models. I have manufacturer and model column in products table.
'Model' filter by default show all the values in the select dropdown. I want to reduce the select options in the 'Model' filter when 'Manufacturer' is selected.
so, for example: When Manufacturer = "Apple" then 'Model' filter should show only Apple 'Models'.
In my Review Resource, I have below code:
/**
* Get the filters available for the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function filters(Request $request)
{
return [
new Manufacturer(),
new Model(),
];
}
Manufacturer Filter code
class Manufacturer extends Filter
{
/**
* The filter's component.
*
* #var string
*/
public $component = 'select-filter';
/**
* Apply the filter to the given query.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Database\Eloquent\Builder $query
* #param mixed $value
*
* #return \Illuminate\Database\Eloquent\Builder
*/
public function apply(Request $request, $query, $value)
{
return $query->whereHas('product', function ($query) use ($value) {
$query->where('manufacturer', $value);
});
}
/**
* Get the filter's available options.
*
* #param \Illuminate\Http\Request $request
*
* #return array
*/
public function options(Request $request)
{
return Product::select('manufacturer')
->withoutGlobalScopes()
->withoutTrashed()
->groupBy('manufacturer')
->orderBy('manufacturer')
->pluck('manufacturer')
->mapWithKeys(function ($manufacturer) {
return [$manufacturer => strtolower($manufacturer)];
})
->toArray();
}
}
Model Filter code
class Model extends Filter
{
/**
* The filter's component.
*
* #var string
*/
public $component = 'select-filter';
/**
* Apply the filter to the given query.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Database\Eloquent\Builder $query
* #param mixed $value
*
* #return \Illuminate\Database\Eloquent\Builder
*/
public function apply(Request $request, $query, $value)
{
return $query->whereHas('product', function ($query) use ($value) {
$query->where('model', $value);
});
}
/**
* Get the filter's available options.
*
* #param \Illuminate\Http\Request $request
*
* #return array
*/
public function options(Request $request)
{
//
//
//I want to add a condition below ->where('manufacturer', $manufacturer)
//
//
return Product::select('model')
->withoutGlobalScopes()
->withoutTrashed()
->groupBy('model')
->orderBy('model')
->pluck('model')
->mapWithKeys(function ($model) {
return [$model => strtolower($model)];
})
->toArray();
}
}
I tried to decode $request to get the filter values but returns null.
I found the library that helps achieve exactly what I wanted.
The library can be found here: https://github.com/awesome-nova/dependent-filter
Once the above library is installed, the two filters can be set as shown below:
Filter A
<?php
namespace App\Nova\Filters;
use Illuminate\Http\Request;
use App\Models\Product;
use AwesomeNova\Filters\DependentFilter;
class Manufacturer extends DependentFilter
{
/**
* Name of filter.
*
* #var string
*/
public $name = 'Manufacturer';
/**
* Attribute name of filter. Also it is key of filter.
*
* #var string
*/
public $attribute = 'manufacturer';
/**
* The filter's component.
*
* #var string
*/
public $component = 'awesome-nova-dependent-filter';
/**
* Apply the filter to the given query.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Database\Eloquent\Builder $query
* #param mixed $value
* #return \Illuminate\Database\Eloquent\Builder
*/
public function apply(Request $request, $query, $value)
{
return $query->whereHas('product', function ($query) use ($value) {
$query->where('manufacturer', $value);
});
}
/**
* Get the filter's available options.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function options(Request $request, array $filters = [])
{
return Product::select('manufacturer')
->pluck('manufacturer')
->mapWithKeys(function ($manufacturer) {
return [$manufacturer => $manufacturer];
})->toArray();
}
Filter B
<?php
namespace App\Nova\Filters;
use App\Models\Product;
use Illuminate\Http\Request;
use AwesomeNova\Filters\DependentFilter;
class Model extends DependentFilter
{
/**
* Name of filter.
*
* #var string
*/
public $name = 'Model';
/**
* Attribute name of filter. Also it is key of filter.
*
* #var string
*/
public $attribute = 'model';
/**
* The filter's component.
*
* #var string
*/
public $component = 'awesome-nova-dependent-filter';
/**
* The filter's dependentOf.
*
* #var array
*/
public $dependentOf = ['manufacturer'];
public $hideWhenEmpty = true;
/**
* Apply the filter to the given query.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Database\Eloquent\Builder $query
* #param mixed $value
* #return \Illuminate\Database\Eloquent\Builder
*/
public function apply(Request $request, $query, $value)
{
return $query->whereHas('product', function ($query) use ($value) {
$query->where('model', $value);
});
}
/**
* Get the filter's available options.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function options(Request $request, array $filters = [])
{
return Product::select('model')
->where('manufacturer', $filters['manufacturer'])
->pluck('model')
->mapWithKeys(function ($model) {
return [$model => $model];
})->toArray();
}
}
Resource File
public function filters(Request $request)
{
return [
Manufacturer::make(),
Model::make(),
];
}
Here's my thoughts and where I'm at. Please tell me if any of this is bad practice!
I have 3 Entities: Article, Author, Reviews:
Relationships:
Article has One Author.
Article has many Reviews.
Author has many Articles.
Review has One Author.
Review has One Article.
I've created a controller called reviewAction where I'm going to build a review form, pass it to a review view, and embed that on my article show view.
Here's my controller so far (it isn't working)
public function reviewAction(Request $request, Article $article)
{
$reviewForm = $this->createFormBuilder($article);
$reviewForm->add('review')->add('title')
->add('author', AuthorType::class, array("label" => FALSE))
->add('rating', ChoiceType::class, array(
'choices' => array('1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' =>'5'),
'expanded' => true,
'multiple' => false
))
->getForm();
if ($reviewForm->isSubmitted() && $reviewForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('article_show', array('id' => $article->getId()));
}
return $this->render('article/review.html.twig', array(
'form' => $reviewForm->createView(),
));
}
First off, is it advisable to build this form here? Seems like I'm working an awful lot with the Review type here in the Articles controllers. Not sure where else I would build it though.
I'm getting this error:
Neither the property "review" nor one of the methods "getReview()", "review()", "isReview()", "hasReview()", "__get()" exist and have public access in class "AppBundle\Entity\Article".
So I'm really stuck and would appreciate anything that anybody can provide.
EDIT
Here's my Article Entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ArticleRepository")
* #UniqueEntity(fields={"name"}, message="Note: That article already existed.")
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=true)
*/
private $description;
/**
* #var string
*
* #ORM\Column(name="thumbnail", type="string", length=255, nullable=true)
*/
private $thumbnail;
/**
* #var \DateTime
*
* #ORM\Column(name="created_date", type="datetime")
*/
private $createdDate;
/**
* #ORM\ManyToOne(targetEntity="Author", inversedBy="articles", cascade={"persist"})
* #ORM\JoinColumn(name="author_id", referencedColumnName="id")
* #Assert\Valid()
*/
private $author;
/**
* #ORM\OneToMany(targetEntity="Review", mappedBy="article")
*/
private $reviews;
public function __construct()
{
$this->reviews = new ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Article
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set description
*
* #param string $description
*
* #return Article
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set thumbnail
*
* #param string $thumbnail
*
* #return Article
*/
public function setThumbnail($thumbnail)
{
$this->thumbnail = $thumbnail;
return $this;
}
/**
* Get thumbnail
*
* #return string
*/
public function getThumbnail()
{
return $this->thumbnail;
}
/**
* Set createdDate
*
* #param \DateTime $createdDate
*
* #return Article
*/
public function setCreatedDate($createdDate)
{
$this->createdDate = $createdDate;
return $this;
}
/**
* Get createdDate
*
* #return \DateTime
*/
public function getCreatedDate()
{
return $this->createdDate;
}
/**
* Set authorId
*
* #param integer $authorId
*
* #return Article
*/
public function setAuthorId($authorId)
{
$this->authorId = $authorId;
return $this;
}
/**
* Get authorId
*
* #return int
*/
public function getAuthorId()
{
return $this->authorId;
}
/**
* Set author
*
* #param \AppBundle\Entity\Author $author
*
* #return Article
*/
public function setAuthor(\AppBundle\Entity\Author $author = null)
{
$this->author = $author;
return $this;
}
/**
* Get author
*
* #return \AppBundle\Entity\Author
*/
public function getAuthor()
{
return $this->author;
}
/**
* Add review
*
* #param \AppBundle\Entity\Review $review
*
* #return Article
*/
public function addReview(\AppBundle\Entity\Review $review)
{
$this->reviews[] = $review;
return $this;
}
/**
* Remove review
*
* #param \AppBundle\Entity\Review $review
*/
public function removeReview(\AppBundle\Entity\Review $review)
{
$this->reviews->removeElement($review);
}
/**
* Get reviews
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getReviews()
{
return $this->reviews;
}
public function __toString() {
return $this->name;
}
}
Here's my Review entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Review
*
* #ORM\Table(name="review")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ReviewRepository")
*/
class Review
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\Column(name="rating", type="smallint")
*/
private $rating;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #ORM\ManyToOne(targetEntity="Author", inversedBy="reviews")
* #ORM\JoinColumn(name="author_id", referencedColumnName="id")
*/
private $author;
/**
* #ORM\ManyToOne(targetEntity="Article", inversedBy="reviews")
* #ORM\JoinColumn(name="article_id", referencedColumnName="id")
*/
private $article;
/**
* #var string
*
* #ORM\Column(name="review", type="text", nullable=true)
*/
private $review;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set rating
*
* #param integer $rating
*
* #return Review
*/
public function setRating($rating)
{
$this->rating = $rating;
return $this;
}
/**
* Get rating
*
* #return int
*/
public function getRating()
{
return $this->rating;
}
/**
* Set title
*
* #param string $title
*
* #return Review
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set authorId
*
* #param integer $authorId
*
* #return Review
*/
public function setAuthorId($authorId)
{
$this->authorId = $authorId;
return $this;
}
/**
* Get authorId
*
* #return int
*/
public function getAuthorId()
{
return $this->authorId;
}
/**
* Set review
*
* #param string $review
*
* #return Review
*/
public function setReview($review)
{
$this->review = $review;
return $this;
}
/**
* Get review
*
* #return string
*/
public function getReview()
{
return $this->review;
}
/**
* Set author
*
* #param \AppBundle\Entity\Author $author
*
* #return Review
*/
public function setAuthor(\AppBundle\Entity\Author $author = null)
{
$this->author = $author;
return $this;
}
/**
* Get author
*
* #return \AppBundle\Entity\Author
*/
public function getAuthor()
{
return $this->author;
}
/**
* Set article
*
* #param \AppBundle\Entity\Author $article
*
* #return Review
*/
public function setArticle(\AppBundle\Entity\Author $article = null)
{
$this->article = $article;
return $this;
}
/**
* Get article
*
* #return \AppBundle\Entity\Author
*/
public function getArticle()
{
return $this->article;
}
}
Here's my article/review.html.twig file
{% extends 'base.html.twig' %}
{% block body %}
<h1>Review Article</h1>
{{ form_start(form) }}
{{ form_widget(form) }}
<input type="submit" value="Create" />
{{ form_end(form) }}
<ul>
<li>
Back to the list
</li>
</ul>
{% endblock %}
Update: This code worked
$reviewForm = $this->createFormBuilder($review)
->add('review', ReviewType::class, array("label" => FALSE))
->setAction($this->generateUrl('fundraiser_review', array('id' =>$fundraiser->getId())))
->getForm();
Thanks!
Because you are creating an Article form, and if you look in your Article Entity, you have a reviews array collection, so thus you need to change the code like so:
$reviewForm = $this->createFormBuilder($article);
$reviewForm->add('reviews')
->add('title')
->add('author', AuthorType::class, array("label" => FALSE))
->add('rating', ChoiceType::class, array(
'choices' => array('1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' =>'5'),
'expanded' => true,
'multiple' => false
))
->getForm();
I think that should solve it. Not 100% certain, but please try it and let us know.
I am fairly new to Symfony but not to PHP web development in general. That said I am having a great deal of difficulty getting a "dynamic" form to submit successfully.
I have been following the details on:
http://symfony.com/doc/current/form/dynamic_form_modification.html#form-events-submitted-data.
I have a Profile entity which has a ManyToOne relationship to the Country and Area entities. Area has a ManyToOne relationship to Country.
On the form, the Area entity select element is dynamically populated depending on the value of the Country select. This works OK. Howver when I submit the form I get the following error:
Warning: spl_object_hash() expects parameter 1 to be object, integer given
500 Internal Server Error - ContextErrorException.
Looking at the stack trace, this seems to stem from methods to flatten my Area entity choice array.
Any and all help is appreciated - I've spent 2 days looking at this so far and I've not got much further!
There are some code details below. If there is any more information I can provide please ask!
Thanks
t2t
My ProfileType class extends AbstractType and contains my buildForm routine which builds the form.
Within ProfileType buildForm I have the following code:
// Location Country & Area
// Start: Dynamic form stuff
// http://symfony.com/doc/current/form/dynamic_form_modification.html#form-events-submitted-data
$builder
->add('locationCountry', EntityType::class, [
'class' => 'AppBundle:Country',
'placeholder' => '',
]);
$formModifier = function (FormInterface $form, Country $country = null) {
$arChoices = array();
if (!is_null($country)) {
$arChoices = $this->em->getRepository('AppBundle:Area')->findByCountry($country);
}
$areas = null === $country ? array() : $arChoices;
$form->add('locationArea', EntityType::class, [
'class' => 'AppBundle:Area',
'placeholder' => '',
'choices' => $areas
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be your entity, i.e. Profile
$data = $event->getData();
$formModifier($event->getForm(), $data->getLocationCountry());
}
);
$builder->get('locationCountry')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
$country = $event->getForm()->getData();
// since we've added the listener to the child, we'll have to pass on
// the parent to the callback functions!
$formModifier($event->getForm()->getParent(), $country);
}
);
// End: Dynamic form stuff
Country is defined as:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Country
*
* #ORM\Table(name="Country")
* #ORM\Entity(repositoryClass="AppBundle\Repository\CountryRepository")
*/
class Country
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100, unique=true)
*/
private $name;
/**
* #var bool
*
* #ORM\Column(name="visible", type="boolean")
*/
private $visible;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Country
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set visible
*
* #param boolean $visible
*
* #return Country
*/
public function setVisible($visible)
{
$this->visible = $visible;
return $this;
}
/**
* Get visible
*
* #return bool
*/
public function getVisible()
{
return $this->visible;
}
public function __toString() {
return $this->getName();
}
}
Area is defined as:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Area
*
* #ORM\Table(name="Area")
* #ORM\Entity(repositoryClass="AppBundle\Repository\AreaRepository")
*/
class Area
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Country")
*/
private $country;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Area
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
public function __toString() {
return $this->name;
}
/**
* Set country
*
* #param \AppBundle\Entity\Country $country
*
* #return Area
*/
public function setCountry(\AppBundle\Entity\Country $country = null)
{
$this->country = $country;
return $this;
}
/**
* Get country
*
* #return \AppBundle\Entity\Country
*/
public function getCountry()
{
return $this->country;
}
}
And the relevant part of Profile is defined as:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Country")
*/
private $locationCountry;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Area")
*/
private $locationArea;
/**
* Set locationArea
*
* #param \AppBundle\Entity\Area $locationArea
*
* #return Profile
*/
public function setLocationArea(\AppBundle\Entity\Area $locationArea = null)
{
$this->locationArea = $locationArea;
return $this;
}
/**
* Get locationArea
*
* #return \AppBundle\Entity\Area
*/
public function getLocationArea()
{
return $this->locationArea;
}
/**
* Set locationCountry
*
* #param \AppBundle\Entity\Country $locationCountry
*
* #return Profile
*/
public function setLocationCountry(\AppBundle\Entity\Country $locationCountry = null)
{
$this->locationCountry = $locationCountry;
return $this;
}
/**
* Get locationCountry
*
* #return \AppBundle\Entity\Country
*/
public function getLocationCountry()
{
return $this->locationCountry;
}
Finally, in my AreaRepository I have the following:
namespace AppBundle\Repository;
use AppBundle\Entity\Area;
/**
* AreaRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class AreaRepository extends \Doctrine\ORM\EntityRepository
{
/**
* #param $oCountry
* #return array
*/
public function findByCountry($oCountry) {
if (is_null($oCountry)) {
return array();
}
$oRepo = $this->getEntityManager()->getRepository('AppBundle:Area');
$oQB = $oRepo->createQueryBuilder('a');
$oQuery = $oQB->where('a.country = :countryId')
->setParameter('countryId', $oCountry)
->getQuery();
$arResult = $oQuery->getArrayResult();
return $arResult;
}
}
You are getting this error because the id of the object is being passed instead of the object itself for persistence.
Look at your own comment: $event->getData() will get you the client data (that is, the ID)
So you are passing the id of the object to your $formModifier function.
And things fail at this line
$arChoices = $this->em->getRepository('AppBundle:Area')->findByCountry($country);
as you pass the id you retrieved to findByCountry.
Bottom line: you are supposed to pass a country object and not just it's id.
I am working on a form with 2 input fields and a submit button. The first field is a simple dropdown (Category) but the other one is a tags-input field(Tag) where u can enter multiple tags at a time. Both fields accept predefined input options only.
The category option values are hardcoded in javascript:
categories = [
{"id": 1, "categoryname": "standard"},
{"id": 2, "categoryname": "premium"},
{"id": 3, "categoryname": "gold"}
];
The options for tag are fetched from the tag table in the database. Here is the screenshot of the database tables:
The Category and Tag entities are associated with Doctrine's ManytoMany bidirectional relationship, with category being the owning side.
Note: I am not using Symfony formType to create the form, instead I've used javascript for that.
The javascript works fine and I get the input data in my controller. Problem is that i've never persisted a ManytoMany relation manually.
Did read the docs but not sure if I missed anything.
Here is the Tag entity (Tag.php) :
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use AppBundle\Entity\Category;
/**
* Tag
*
* #ORM\Table(name="tag")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TagRepository")
*/
class Tag {
/**
* #var int
*
* #ORM\Column(name="Id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*
* #var string
*
* #ORM\Column(name="TagName", type="string")
*/
protected $tagname;
/**
* #ORM\ManyToMany(targetEntity="Category", mappedBy="tags")
*/
protected $categories;
/**
* #return ArrayCollection
*/
public function __construct() {
$this->categories = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set id
*
* #return Tag
*/
public function setId($id)
{
return $this->id = $id;
}
/**
* Set tagname
*
* #param string $tagname
* #return Tag
*/
public function setTagname($tagname)
{
$this->tagname = $tagname;
return $this;
}
/**
* Get tagname
*
* #return string
*/
public function getTagname()
{
return $this->tagname;
}
/**
* Add categories
*
* #param \AppBundle\Entity\Category $categories
* #return Tag
*/
public function addCategory(\AppBundle\Entity\Category $categories)
{
$this->categories[] = $categories;
return $this;
}
/**
* Remove categories
*
* #param \AppBundle\Entity\Category $categories
*/
public function removeCategory(\AppBundle\Entity\Category $categories)
{
$this->categories->removeElement($categories);
}
/**
* Get categories
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
}
Here is the Category entity (Category.php) :
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use AppBundle\Entity\Tag;
/**
* Category
*
* #ORM\Table(name="category")
* #ORM\Entity(repositoryClass="AppBundle\Repository\CategoryRepository")
*/
class Category {
/**
* #var int
*
* #ORM\Column(name="Id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*
* #var string
*
* #ORM\Column(name="CategoryName", type="string")
*/
protected $categoryname;
/**
*
* #var string
*
* #ORM\Column(name="Description", type="string")
*/
protected $description;
/**
* #ORM\ManyToMany(targetEntity="Tag", cascade={"persist"}, inversedBy="categories")
*/
protected $tags;
/**
* #return ArrayCollection
*/
public function __construct() {
$this->tags = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set id
*
* #return Category
*/
public function setId($id) {
return $this->id = $id;
}
/**
* Set categoryname
*
* #param string $categoryname
* #return Category
*/
public function setCategoryname($categoryname) {
$this->categoryname = $categoryname;
return $this;
}
/**
* Get categoryname
*
* #return string
*/
public function getCategoryname() {
return $this->categoryname;
}
/**
* Set description
*
* #param string $description
* #return Category
*/
public function setDescription($description) {
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription() {
return $this->description;
}
/**
* Add tags
*
* #param \AppBundle\Entity\Tag $tags
* #return Category
*/
public function addTag(\AppBundle\Entity\Tag $tags) {
$this->tags[] = $tags;
return $this;
}
/**
* Remove tags
*
* #param \AppBundle\Entity\Tag $tags
*/
public function removeTag(\AppBundle\Entity\Tag $tags) {
$this->tags->removeElement($tags);
}
/**
* Get tags
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTags() {
return $this->tags;
}
}
Here is the controller(DefaultController.php):
/**
* #Route("/formsubmit", options={"expose"=true}, name="my_route_to_submit")
*/
public function submitAction(Request $request) {
$jsonString = file_get_contents('php://input');
$form_data = json_decode($jsonString, true);
$em = $this->getDoctrine()->getManager();
// set category details
$categoryId = $form_data[0]['id'];
$category = $em->getRepository('AppBundle:Category')->findOneById($categoryId);
// set tags
$len = count($form_data[1]);
for ($i = 0; $i < $len; $i++) {
$tagId = $form_data[1][$i]['id'];
$tag = $em->getRepository('AppBundle:Tag')->findOneById($tagId);
$category->addTag($tag);
}
// persist/save in database
$em->persist($category);
$em->flush();
}
The $form_data is an array with the input category and added tags detail. It looks like this:
$form_data = [
['id' => 3, 'categoryname' => 'gold'],
[
['id' => 1, 'tagname' => 'wifi'],
['id' => 4, 'tagname' => 'geyser'],
['id' => 2, 'tagname' => 'cable']
]
];
Still it doesn't persist. The var_dump($category); display the selected category object with category id and categoryname, but the associated tags property is empty.
Here is the screenshot of the output:
Any Ideas?
Quick question on the side: Do I need to add cascade={"persist"} to both the sides of relationship definition here?
EDIT: Here, I've hard-coded $form_data instead of using input data as I did above. The DefaultController.php :
/**
* #Route("/formsubmit", options={"expose"=true}, name="my_route_to_submit")
*/
public function submitAction(Request $request) {
$form_data = [
['id' => 3, 'categoryname' => 'gold'],
[
['id' => 1, 'tagname' => 'wifi'],
['id' => 4, 'tagname' => 'geyser'],
['id' => 2, 'tagname' => 'cable']
]
];
$em = $this->getDoctrine()->getManager();
// set category details
$categoryId = $form_data[0]['id'];
$category = $em->getRepository('AppBundle:Category')->findOneById($categoryId);
// set tags
$len = count($form_data[1]);
for ($i = 0; $i < $len; $i++) {
$tagId = $form_data[1][$i]['id'];
$tag = $em->getRepository('AppBundle:Tag')->findOneById($tagId);
// $tag->addCategory($category);
$category->addTag($tag);
}
var_dump($category);
exit;
// persist/save in database
$em->persist($category);
$em->flush();
}
The controller output:
As you can see the tags property of the category object is still empty.
Hope this'll help understand the issue better.
Awaiting response...
When getting the appropriate tag entity by doing
$tag = $em->getRepository('AppBundle:Tag')->findOneById($tagId);
Isn't the value of $tag an array of collections?
So possibly do the following?
$category->addTag($tag[0]);
I' searched all over the Internet but I've not found a definite answer to this question.
I have the following entities:
/**
* #ORM\Entity
* #ORM\Table(name="category")
*/
class Category
{
/*
* ...
*/
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
protected $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
/**
* Add product
*
* #param \AppBundle\Entity\Product $product
*
* #return Category
*/
public function addProduct(\AppBundle\Entity\Product $product)
{
$this->products[] = $product;
return $this;
}
/**
* Remove product
*
* #param \AppBundle\Entity\Product $product
*/
public function removeProduct(\AppBundle\Entity\Product $product)
{
$this->products->removeElement($product);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
/**
* #ORM\Entity
* #ORM\Table(name="product")
*/
class Product
{
/*
* ...
*/
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
/**
* #ORM\Column(type="boolean")
*/
protected $active;
/**
* Set category
*
* #param \AppBundle\Entity\Category $category
*
* #return Product
*/
public function setCategory(\AppBundle\Entity\Category $category = null)
{
$this->category = $category;
return $this;
}
/**
* Get category
*
* #return \AppBundle\Entity\Category
*/
public function getCategory()
{
return $this->category;
}
/**
* Set active
*
* #param boolean $active
*
* #return Product
*/
public function setActive($active)
{
$this->active= $active;
return $this;
}
/**
* Get active
*
* #return boolean
*/
public function getActive()
{
return $this->active;
}
}
I only want to do this:
$em = $this->getDoctrine()->getManager();
$categories = $em->getRepository('AppBundle\Entity\Category')->findAll();
$json = $serializer->serialize($categories, 'json');
and in the json result I want to make sure that all categories have their associated products filtered by status=true.In other words I want to get the results in the front end as an array of category objects with each category having an array of products that are active only.
I know it can be done trivially in Django.
Can it be done in doctrine?If yes how.
Strait answers please.
SOLUTION I'VE USED
$qb = $em->createQueryBuilder()
->select('c')
->from('AppBundle:Category','c')
->leftJoin('c.products','p','WITH','p.status = :status')
->addSelect('p')
->setParameter('status', true);