Skip to content
Advertisement

Foreign Key in Doctrine (Symfony). How do I bind a new child to an already existing parent?

There is a database of Regions and Cities. The relationship between the regions and cities tables is one-to-many. Below is my code for adding a new city to the database. It gives an error:

https://i.stack.imgur.com/LZPjz.png

I found only one solution on the Internet – to use “$ entity_manager-> persist ()” not only on the city, but also on the region. But in this case, Doctrine duplicates the region already existing in the database.

The Symfony documentation does cover adding a new child along with a new parent, what I don’t need.

In Controller:

/**
 * @Route("/admin/insert/city", name="city")
 */
public function city(Request $request)
{
    // get list of regions
    $regions_in_database = $this->getDoctrine()->getRepository(Region::class)->findAll();

    // add the regions into an array
    $regions = [];
    foreach ($regions_in_database as $region) {
        $regions[(string)$region->getName()] = clone $region;
    }

    // Create Form
    $city = new City();
    $insert_city = $this->createForm(InsertCityType::class, $city, [
        'regions_array' => $regions,
    ]);

    // Checking for the existence of regions
    if (!$regions_in_database) {
        $insert_city->addError(new FormError("Отсутствуют регионы!
            Пожалуйста, перейдите по ссылке /insert/region и добавьте соответствующий регион"));
    }

    // Отправка формы
    $insert_city->handleRequest($request);
    if ($insert_city->isSubmitted() && $insert_city->isValid()) {

        // Check if there is already such a city in the database
        $repository = $this->getDoctrine()->getRepository(City::class);
        $city_in_database = $repository->findOneBy(
            [
                'name' => $city->getName(),
                'region' => $city->getRegion(),
            ]
        );

        // If the city is in the database, throw an error
        if ($city_in_database) {
            $insert_city->addError(new FormError('Такой город уже существует!'));
        }
        else {
            $entity_manager = $this->getDoctrine()->getManager();
            $entity_manager->persist($city);
            $entity_manager->flush();

            $city = new City();
            $insert_city = $this->createForm(InsertCityType::class, $city, [
                'regions_array' => $regions,
            ]);
        }
    }

    return $this->render('admin/insert/city/city.html.twig', [
        'insert_city' => $insert_city->createView(),
    ]);
}

Form Class:

class InsertCityType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('region', ChoiceType::class, [
                'choices'  => $options['regions_array'],
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Cities::class,
            'regions_array' => null,
        ]);

        $resolver->setAllowedTypes('regions_array', 'array');
    }
}

Form:

https://i.stack.imgur.com/1hGv0.png

Region Class:

/**
 * Regions
 *
 * @ORMTable(name="regions")
 * @ORMEntity
 */
class Regions
{
    /**
     * @var int
     *
     * @ORMColumn(name="id", type="integer", nullable=false)
     * @ORMId
     * @ORMGeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORMColumn(name="name", type="string", length=255, nullable=false)
     */
    private $name;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }
}

City Class:

/**
 * Cities
 *
 * @ORMTable(name="cities", indexes={@ORMIndex(name="region", columns={"region"})})
 * @ORMEntity
 */
class Cities
{
    /**
     * @var int
     *
     * @ORMColumn(name="id", type="integer", nullable=false)
     * @ORMId
     * @ORMGeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORMColumn(name="name", type="string", length=255, nullable=false)
     */
    private $name;

    /**
     * @var Regions
     *
     * @ORMManyToOne(targetEntity="Regions", inversedBy="cities")
     * @ORMJoinColumns({
     *   @ORMJoinColumn(name="region", referencedColumnName="id")
     * })
     */
    private $region;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getRegion(): ?Regions
    {
        return $this->region;
    }

    public function setRegion(?Regions $region): self
    {
        $this->region = $region;

        return $this;
    }
}

Advertisement

Answer

Follow those steps: create a new city, get the right region and then add the new city to the region and save it to your database.

For that, add the list of cities in your Region entity like this:

/**
 * @var ArrayCollection
 *
 * @ORMOneToMany(targetEntity="City", mappedBy="region", cascade={"persist"})
 */
private $cities; // This attribut will not appear in your database

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

Generate assets and getters with this command php bin/console make:entity --regenerate.

Now, you are able to add a city in a region:

$theRegion = fetchMyRegion(); // Fetch the region that you want
$theRegion->addCity($myCity); // Don't forget 'cities' attribut is a ArrayCollection
$entity_manager->persist($theRegion); // City object saves automatically in your database
$entity_manager->flush(); // The new city is now created and linked to the region
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement