Doctrine - import from json - relations between inexistent rows

I’m using Symfony4 and I’m trying to populate a mysql database with some data from a JSON.

The database is something like this:

movies
 - id
 - title
 - description
 etc etc

people
 - id
 - name
 - role

relations
 - id
 - subjectid (manytoone relation)
 - type
 - typeid

The JSON is something like this

{
    "title": "Friday the 13th Part II",
    "description": "Some data",
    "actors": [
      {
        "name": "John Furey"
      },
      {
        "name": "Adrienne King"
      },
      {
        "name": "Amy Steel"
      }
    ],
    "cert": "18",
    "genres": [
      "Horror"
    ],
  }

I made the entities, I know how to insert the flat elements from the array, but I don’t know how to insert the actors in the people table and make an entry inside relations table with the actor id, the type(actor/director/genres), and the subjectid(movie id).
I’ve read the symfony and doctrine documentation and I didn’t find a solution.

protected function sendTODB(array $movie){

		//Entity Manager
		$em = $this->getDoctrine()->getManager();

		$movie = new Movies();
		$movie->setTitle($movie['title']);
		$movie->setDescription($movie['description']);

		$people= new People();
		$people->setRole();



	}

Thank you.

Could you post the code for all three entities please?

Entity/Movies.php

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\MoviesRepository")
 */
class Movies
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

    /**
     * @ORM\Column(type="text")
     */
    private $description;

 

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\RelTable", mappedBy="subjectid")
     */
    private $relations;


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

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

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(string $title): self
    {
        $this->title = $title;

        return $this;
    }

    public function getDescription(): ?string
    {
        return $this->description;
    }

    public function setDescription(string $description): self
    {
        $this->description= $description;

        return $this;
    }

    /**
     * @return Collection|RelTable[]
     */
    public function getRelations(): Collection
    {
        return $this->relations;
    }

    public function addRelation(RelTable $relation): self
    {
        if (!$this->relations->contains($relation)) {
            $this->relations[] = $relation;
            $relation->setSubjectid($this);
        }

        return $this;
    }

    public function removeRelation(RelTable $relation): self
    {
        if ($this->relations->contains($relation)) {
            $this->relations->removeElement($relation);
            // set the owning side to null (unless already changed)
            if ($relation->getSubjectid() === $this) {
                $relation->setSubjectid(null);
            }
        }

        return $this;
    }
}

Entity/People.php

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\PeopleRepository")
 */
class People
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

    /**
     * @ORM\Column(type="string", length=20)
     */
    private $role;

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

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

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

        return $this;
    }

    public function getRole(): ?string
    {
        return $this->role;
    }

    public function setRole(string $role): self
    {
        $this->role = $role;

        return $this;
    }
}

Entity/RelTable.php

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\RelTableRepository")
 */
class RelTable
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Movies", inversedBy="relations")
     * @ORM\JoinColumn(nullable=false)
     */
    private $subjectid;

    /**
     * @ORM\Column(type="string", length=50)
     */
    private $type;

    /**
     * @ORM\Column(type="integer")
     */
    private $typeid;

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

    public function getSubjectid(): ?Movies
    {
        return $this->subjectid;
    }

    public function setSubjectid(?Movies $subjectid): self
    {
        $this->subjectid = $subjectid;

        return $this;
    }

    public function getType(): ?string
    {
        return $this->type;
    }

    public function setType(string $type): self
    {
        $this->type = $type;

        return $this;
    }

    public function getTypeid(): ?int
    {
        return $this->typeid;
    }

    public function setTypeid(int $typeid): self
    {
        $this->typeid = $typeid;

        return $this;
    }


}

I don’t understand what the RelTable model is for, and I also don’t understand what the relations database table is for. There seems to be nothing in the JSON that needs those. Also, I don’t see anywhere in the JSON a reference to a role for an actor. What’s that?

As for the general gist, using Doctrine, you would do something like this

Movie model:

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

class Movie
{
    /**
     * @var int
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

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

    /**
     * @var array|Actor[]|Collection
     * @ORM\OneToMany(target="App\Entity\Actor")
     */
    private $actors;
     
    public function __construct(string $title, string $description)
    {
        $this->title = $title;
        $this->description = $description;
        $this->actors = new ArrayCollection();
    }

    public function addActor(Actor $actor)
    {
        $this->actors->add($actor);
    }
}

Actor model:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

class Actor
{
    /**
     * @var int
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

    public function __construct(string $name)
    {
        $this->nam = $name;
    }
}

Controller:

<?php

$em = $this->getDoctrine()->getManager();

$data = json_decode($jsonString, true);

foreach ($data as $movieData) {
    $movie = new Movie($movieData['title'], $movieData['description']);

    foreach ($movieData['actors'] as $actorData) {
        $actor = new Actor($actorData['name']);

        $em->persist($actor);
        $movie->addActor($actor);
    }

    $em->persist($movie);
    $em->flush();
}

Note that I’ve changed two things quite drastically:

  1. All model names are singular, as a single class represents one movie, not several movies, so the name movies is just plain confusing
  2. The models have constructors with required fields. For example you cannot create a movie object without supplying a name, since a movie without a name doesn’t make any sense.

HTH :slight_smile:

1 Like

Sorry, I wasn’t too clear.
It was just an example, above I simplified and changed the names. The JSON is bigger and it has multiple arrays, I just wanted to show that it is a multidimensional array.
The relations table is for relations between “people” and movies. And is not limited just to people, is for genres too, or media.

If I have an actor that is in multiple movies I would have to have duplicate data in my DB. This way I store the people in the people table, the movie in movie table and make relation between them in the relations table.

For example

Movie1 - actor Meryl Streep
Movie 2 - actor Meryl Streep
The DB would be something like this

movies
 - 1(id)  | Movie1 (title) | Balblabla(description)
 - 2(id)  | Movie2 (title) | Balblabla(description)

people
 - 1(id)  |  Meryl Streep | actor(role)
 - 2(id)  |  Martin Scorsese | director(role)

this is the relation between the movie and the actor
relations
 - 1(id) 1(movieid - Movie1) | actor(type) | 1(typeid - people id of meryl streep)
 - 2(id) 1(movieid - Movie1) | director(type) | 2(typeid - people id of martin scorsese)

Thank you for your response.

Right, but then I would say the role column on the people table is out of place, since one person can be actor in one movie, and be a director in another movie, or indeed actor, writer and director in the same movie (like Quintin Tarantino in Reservoir Dogs).

So I would suggest to ditch that column, and keep that on the relations table instead.

A rough outline of the code would be

foreach (movies as movieData)
    movie = new Movie(movieData)
    foreach (actors as actorData)
        actor = actorRepository.findActor(actorData)
        if !actor
            actor = new Actor(actorData)
        relation = new Relation(movie, actor, relationType)
        save(actor)
        save(relation)
    save(movie)

I’ll leave it to you to translate that to Doctrine code. Shouldn’t be too hard. And just holler if you get stuck :slight_smile:

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.