I am busy developing an action for parent
that should add a number of children given by the user input.
The children have 3 properties, and combining them, each child should always be unique.
I make use of Symfony and Doctrine and my basic parent class looks like this:
class Parent
{
/**
* @var Child[]|ArrayCollection
*
* @ORM\OneToMany(targetEntity="Child", mappedBy="parent")
* @ORM\OrderBy({"dateCreated": "DESC"})
*/
private $childs;
/**
* Add child
*
* @param \AppBundle\Entity\Child $child
*
* @return Parent
*/
public function addChild(\AppBundle\Entity\Child $child)
{
$this->childs[] = $child;
$child->setParent($this);
return $this;
}
/**
* Remove child
*
* @param \AppBundle\Entity\Child $child
*/
public function removeChild(\AppBundle\Entity\Child $child)
{
$this->childs->removeElement($child);
}
/**
* Get childs
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getChilds()
{
return $this->childs;
}
}
My child class look like this (again really basic):
class Child
{
/**
* @var int
*
* @ORM\Column(name="cupboard", type="integer")
*/
private $cupboard;
/**
* @var int
*
* @ORM\Column(name="shelf", type="integer")
*/
private $shelf;
/**
* @var int
*
* @ORM\Column(name="item", type="integer")
*/
private $item;
/**
* @var Parent
*
* @ORM\ManyToOne(targetEntity="Parent", inversedBy="childs")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
* })
*/
private $parent;
/**
* Set cupboard
*
* @param string $cupboard
*
* @return Child
*/
public function setCupboard($cupboard)
{
$this->cupboard = $cupboard;
return $this;
}
/**
* Get cupboard
*
* @return int
*/
public function getCupboard()
{
return $this->cupboard;
}
/**
* Set shelf
*
* @param string $shelf
*
* @return Child
*/
public function setShelf($shelf)
{
$this->shelf = $shelf;
return $this;
}
/**
* Get shelf
*
* @return int
*/
public function getShelf()
{
return $this->shelf;
}
/**
* Set item
*
* @param string $item
*
* @return Child
*/
public function setItem($item)
{
$this->item = $item;
return $this;
}
/**
* Get item
*
* @return int
*/
public function getItem()
{
return $this->item;
}
/**
* Set parent
*
* @param Parent $parent
*
* @return Child
*/
public function setParent(Parent $parent)
{
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* @return Parent
*/
public function getParent()
{
return $this->parent;
}
}
Then I have an action per parent
object (kind of the same as an edit action) that has to create children for it. When I click the link (for the specific parent) a form is generated that has three input fields:
The user then has to specify with integers how many items he wants to add in what cupboard and what shelf. If the item (integer) already exist in that given cupboard on that given shelf it should not make it again, but the first next available integer should be used for item.
How can I make this as simple as possible? I understand I can use the addChild
function from the Parent
class, but I'm not sure how.
What I have tried so far is to create an array and group all items according to cupboard and shelf and then if the item does not exist in that array it should be created.
This is my code:
public function addItemAction(Request $request, $id = null){
$parent = $this->admin->getSubject();
if (!$parent) {
throw new NotFoundHttpException(sprintf('Unable to find the object with id: %s', $id));
}
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(AddItemType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$kids = $popUnit->getChilds();
$formParams = $request->request->get('add_item');
$units = array();
foreach ($kids as $kid) {
$units[$kid->getCupboard()][$kid->getShelf()][$kid->getItem()] = $kid->getItem();
}
$givenShelf = $units[$formParams['cupboard']][$formParams['shelf']];
for ($i = 1; $i <= $formParams['itemAmount']; $i++) {
if (!in_array($i, $givenShelf)) {
$child = new Child();
$child->setParent($parent);
$child->setCupboard($formParams['cupboard']);
$child->setShelf($formParams['shelf']);
$child->setItem($i);
$em->persist($child);
$em->flush();
}
}
return new RedirectResponse($this->admin->generateUrl('show', array('id' => $parent->getId())));
}
return $this->render('AppBundle:Parent:add_childs.html.twig', array(
'form' => $form->createView(),
'parent' =>$parent,
));
}
For extra information, this is how my form builder looks:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('cupboard', NumberType::class)
->add('shelf', NumberType::class)
->add('itemAmount', NumberType::class, array('label' => 'Number of Items'))
;
}
How can I make this action as simple as possible with the fact to make sure only unique items is added to a shelf in a cupboard for the parent. I can't change the properties nor the classes. I have to work with this. I don' want to use any other complex way like creating any listeners or other until functions.
I hope I've explained my situation well and I hope somebody can help me with some good feedback.
When discussing objects a "parent-child" relationship implies a hierarchy of objects which can be represented as a tree with parents possessing strong references to their children. If you can draw that tree of objects a "parent" would be closer to the root while a "child" would be closer to a leaf node.
The object to which a given property or method belongs.
To parent objects, select at least two objects (select the Child Objects first, and select the Parent Object last), and press Ctrl-P . The Set Parent To menu will pop up allowing you to select from one of several possible different parenting types.
You are correct that you can modify the addChild
function of the Parent
class like this:
/**
* Add child
*
* @param \AppBundle\Entity\Child $child
*
* @return Parent
*/
public function addChild(Child $child)
{
if ( ! is_array($this->childs) || ! in_array($child, $this->childs)) {
$this->childs[] = $child;
$child->setParent($this);
}
return $this;
}
This conditional simply states that if there are no children yet, add the child or if the child is not already in the $this->childs
array then add it.
Because you access to $childs
via removeChild
as a Doctrine collection:
/**
* Remove child
*
* @param \AppBundle\Entity\Child $child
*/
public function removeChild(\AppBundle\Entity\Child $child)
{
$this->childs->removeElement($child);
}
So, in the constructor, you should initialize your childs collection first.
public function __construct()
{
$this->childs = new ArrayCollection();
}
To avoid duplicate children, you need to checks existing before add to collection.
/**
* Add child
*
* @param \AppBundle\Entity\Child $child
*
* @return Parent
*/
public function addChild(\AppBundle\Entity\Child $child)
{
if (!$this->childs->contains($child)) {
$this->childs->add($child);
$child->setParent($this);
}
return $this;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With