1

I'm trying to embed a collection of forms in a symfony2 application. I have to simple entities : Shop and Address whith a Shop which has multiple address. I follow the Symfony2 documentation but I get the error :

Neither the property "address" nor one of the methods "getAddress()", "address()", "isAddress()", "hasAddress()", "__get()" exist and have public access in class "AppBundle\Entity\Address". 500 Internal Server Error - NoSuchPropertyException

It seems to me that it is trying to access the address preoperty of my Address Entity.

Here is my Shop Entity

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\Address;
use UserBundle\Entity\Seller;

/**
 * Shop
 *
 * @ORM\Table(name="app_shop")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\ShopRepository")
 */
class Shop
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     * @ORM\Column(name="shopName", type="string", length=255)
     */
    private $shopName;

    /**
     * @var string
     * @ORM\Column(name="description", type="text", nullable=true)
     */
    private $description;

    /**
     * @var string
     * @ORM\Column(name="ownerName", type="string", length=255)
     */
    private $ownerName;

    /**
     * @ORM\ManyToOne(targetEntity="UserBundle\Entity\Seller", cascade={"refresh"}, fetch="EAGER")
     * @ORM\JoinColumn(nullable=false, onDelete="NO ACTION")
     * @Assert\Valid()
     */
    private $owner;

    /**
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Address", mappedBy="shop")
     * @Assert\Valid()
     */
    private $address;

    /**
     * @ORM\Column(type="string", nullable=true)
     * @Assert\Length(
     *      min = 9,
     *      max = 10,
     *      minMessage = "Le numéro siret doit contenir 10 chiffres",
     *      maxMessage = "Le numéro siret doit contenir 10 chiffres"
     * )
     */
    private $siret;

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Image")
     * @ORM\JoinColumn(nullable=true)
     */
    private $image;


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

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set shopName
     *
     * @param string $shopName
     * @return Shop
     */
    public function setShopName($shopName)
    {
        $this->shopName = $shopName;

        return $this;
    }

    /**
     * Get shopName
     *
     * @return string 
     */
    public function getShopName()
    {
        return $this->shopName;
    }

    /**
     * Set shopName
     *
     * @param string $description
     * @return Shop
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     * @return string 
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set ownerName
     *
     * @param string $ownerName
     * @return Shop
     */
    public function setOwnerName($ownerName)
    {
        $this->ownerName = $ownerName;
        return $this;
    }

    /**
     * Get ownerName
     *
     * @return string 
     */
    public function getOwnerName()
    {
        return $this->ownerName;
    }

    /**
     * Set siret
     *
     * @param string $siret
     * @return Shop
     */
    public function setSiret($siret)
    {
        $this->siret = $siret;
        return $this;
    }

    /**
     * Get siret
     *
     * @return string
     */
    public function getSiret()
    {
        return $this->siret;
    }


    /**
     * Set address
     *
     * @param ArrayCollection $address
     *
     * @return Address
     */
    public function setAddress(ArrayCollection $address)
    {
        $this->address = $address;
        return $this;
    }

    /**
     * Get address
     *
     * @return \AppBundle\Entity\Address
     */
    public function getAddress()
    {
        return $this->address;
    }

    /**
     * Set owner
     *
     * @param \UserBundle\Entity\Seller $owner
     *
     * @return Owner
     */
    public function setOwner(Seller $owner = null)
    {
        $this->owner = $owner;
        return $this;
    }

    /**
     * Get owner
     *
     * @return \UserBundle\Entity\Sellers
     */
    public function getOwner()
    {
        return $this->owner;
    }


    /**
     *
     * @param Image $image
     * @return \AppBundle\Entity\Shop
     */
    public function setImage(Image $image)
    {
        $this->image = $image;
        return $this;
    }

    /**
     *
     */
    public function getImage()
    {
        return $this->image;
    }

}

Here is my Address Entity

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;


/**
 * Adress
 *
 * @ORM\Table(name="app_address")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\AddressRepository")
 */
class Address
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="street", type="string", length=255)
     * @Assert\Length(  min = 5 , 
     *                  max = 200,
     *                  minMessage = "L'adresse doit faire au minimum {{ limit }} caractères.",
     *                  maxMessage = "L'adresse doit faire au maximum {{ limit }} caractères.")
     * 
     */
    private $street;

    /**
     * @ORM\Column(type="string", length=5)
     * @Assert\Regex(
     *     pattern="/^\d{4,5}$/",
     *     match=true,
     *     message="Le format n'est pas correct"
     * )
     */
    private $postalCode;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $city;

    /**
     * @var string
     *
     * @ORM\Column(name="country", type="string", length=255 , nullable=true)
     * @Assert\Length(  min = 3 ,
     *                  max = 50,
     *                  minMessage = "Le pays doit faire au minimum {{ limit }} caractères.",
     *                  maxMessage = "L'adresse doit faire au maximum {{ limit }} caractères.")
     *
     */
    private $country;

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Shop", inversedBy="address")
     * @ORM\JoinColumn(name="shop_id", referencedColumnName="id")
     */
    private $shop;

    /**
     * Get id
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set street
     *
     * @param string $street
     *
     * @return Address
     */
    public function setStreet($street)
    {
        $this->street = $street;

        return $this;
    }

    /**
     * Get street
     *
     * @return string
     */
    public function getStreet()
    {
        return $this->street;
    }

    /**
     * Set postalCode
     * @return Address
     */
    public function setPostalCode($postalCode)
    {
        $this->postalCode = $postalCode;
        return $this;
    }

    /**
     * Get postalCode
     */
    public function getPostalCode()
    {
        return $this->postalCode;
    }

    /**
     *
     * @param string
     * @return \AppBundle\Entity\Address
     */
    public function setCity($city = null)
    {
        $this->city = $city;
        return $this;
    }

    /**
     *
     */
    public function getCity()
    {
        return $this->city;
    }

    /**
     *
     * @param string
     * @return \AppBundle\Entity\Address
     */
    public function setCountry($country = null)
    {
        $this->country = $country;
        return $this;
    }

    /**
     *
     */
    public function getCountry()
    {
        return $this->country;
    }

    /**
     *
     * @param Shop
     * @return \AppBundle\Entity\Address
     */
    public function setShop($shop = null)
    {
        $this->shop = $shop;
        return $this;
    }

    /**
     *
     */
    public function getShop()
    {
        return $this->shop;
    }

    public function __toString() {
        return $this->street." ".$this->postalCode." ".$this->city;
    }
}

I created two formType in order to manage my entities :

ShopType.php

<?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

use AppBundle\Form\AddressType;
use AppBundle\Entity\Shop;
use AppBundle\Entity\Address;

class ShopType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('shopName', TextType::class, array('label' => 'Nom du magasin *', 'required' => true, 'error_bubbling' => true))
            ->add('ownerName', TextType::class, array('label' => 'Nom du gérant *', 'required' => true, 'error_bubbling' => true))

            ->add('address', CollectionType::class, array(  'entry_type' => AddressType::class,
                                                            'allow_add' => true,
                                                            'label' => 'Adresse *', 
                                                            'required' => true
            ));
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
                'data_class' => Shop::class,
        ));
    }
}

AddressType.php

<?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\OptionsResolver\OptionsResolver;

use AppBundle\Entity\Address;

class AddressType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('address', TextType::class, array('label' => 'Adresse*', 'required' => true))
            ->add('CodePostal', TextType::class, array('label' => 'Code postal*', 'required' => true, 'error_bubbling' => true))
            ->add('Ville', TextType::class, array('label' => 'Ville', 'required' => false, 'error_bubbling' => true))
            ->add('Pays', 'choice', array(
                    'choices'   => array(
                            'FR'   => 'France',
                            'SU'   => 'Suisse',
                            'BE'    => 'Belgique'
                    )
            ))
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
                'data_class' => Address::class,
        ));
    }

    public function getName()
    {
        return 'addresse';
    }
}

In my controller, I'm instanciating my form as following :

$shop = $shopRepo->findOneByOwner($user);
if ($shop == null){
    $shop = new Shop();
}
$form = $this->createForm(ShopType::class , $shop);

1 Answer 1

3

This error is occurred due to this line at AddressType class:

->add('address', TextType::class, array('label' => 'Adresse*', 'required' => true))

You are trying to access address property of Address entity, but it doesn't contain it. The same is related to other fields: CodePostal => postalCode etc

Also at your Shop entity you have address field annotated as OneToMany relation which is ok, and at the same time you have setAddress(ArrayCollection $address) method, but instead you should have:

public function addAddress(Address $address)
{
    $this->address->add($address);

    return $this;
}

and optional

public function removeAddress(Address $address)
{
    $this->address->remove($address);

    return $this;
}

Btw I suggest to rename property address into addresses to highlight that it should contain collection but not single entity.

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

1 Comment

Thanks a lot for your explanation. I'm glad you "validated" my OneToMany relation, I was not sure about it. Also, thanks for correcting my Shop entity !

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.