Search code examples
doctrine-ormzend-framework2entityleft-joinresultset

Doctrine Hydrate joined Entities into one array-entry


I'm currently encountering some problems with Doctrine Hydration. I'm using ZF2 to build an API and therefore i need to build some queries that make use of joins.

For now they look somewhat like this:

$qb->select(array('r, a'))
   ->from('Release\Entity\Release', 'r')
   ->leftJoin(
       'Artist\Entity\Artist', a',
       \Doctrine\ORM\Query\Expr\Join::WITH, 'a.id = r.artist'
     )
   ->orderBy('r.id', 'ASC');

So if i hydrate the Result with AbstractQuery::HYDRATE_ARRAY the result will be an extra array entry for each joined table/entity.

Array
(
    [0] => Array
        (
            [id] => 1
            [artist] => 1
            [title] => A super nice Release
            [digital_release] => DateTime Object
                (
                    [date] => 2014-01-25 00:00:00.000000
                    [timezone_type] => 3
                    [timezone] => Europe/Berlin
                )

            [physical_release] => DateTime Object
                (
                    [date] => 2014-01-25 00:00:00.000000
                    [timezone_type] => 3
                    [timezone] => Europe/Berlin
                )

        )

    [1] => Array
        (
            [id] => 1
            [first_name] => John
            [last_name] => Doe
            [artist_name] => JD and the Beat-Machines
            [created_at] => DateTime Object
                (
                    [date] => 2015-04-17 13:16:18.000000
                    [timezone_type] => 3
                    [timezone] => Europe/Berlin
                )

            [updated_at] => DateTime Object
                (
                    [date] => 2015-04-17 13:16:18.000000
                    [timezone_type] => 3
                    [timezone] => Europe/Berlin
                )

        )

    [2] => Array
        (
            [id] => 2
            [artist] => 14
            [title] => Some other nice album
            [digital_release] => DateTime Object
                (
                    [date] => 2014-02-01 00:00:00.000000
                    [timezone_type] => 3
                    [timezone] => Europe/Berlin
                )

            [physical_release] => DateTime Object
                (
                    [date] => 2014-02-01 00:00:00.000000
                    [timezone_type] => 3
                    [timezone] => Europe/Berlin
                )

        )

    [...]
)

As you can see the artist of the corresponding release [0] is stored into array-key [1].

If I finally print the result as new JsonModel($result) this doesn't seem a workable way to me.

What i want to achieve is some result like this

Array
(
    [0] => Array
        (
            [id] => 1
            [artist] => Array
                     (
                         [id] => 1
                         [first_name] => John
                         [last_name] => Doe
                         [artist_name] => JD and the Beat-Machines
                         [created_at] => DateTime Object
                             (
                                 [date] => 2015-04-17 13:16:18.000000
                                 [timezone_type] => 3
                                 [timezone] => Europe/Berlin
                              )

                         [updated_at] => DateTime Object
                              (
                                  [date] => 2015-04-17 13:16:18.000000
                                  [timezone_type] => 3
                                  [timezone] => Europe/Berlin
                              )

                      )
            [title] => A super nice Release
            [digital_release] => DateTime Object
                (
                    [date] => 2014-01-25 00:00:00.000000
                    [timezone_type] => 3
                    [timezone] => Europe/Berlin
                )

            [physical_release] => DateTime Object
                (
                    [date] => 2014-01-25 00:00:00.000000
                    [timezone_type] => 3
                    [timezone] => Europe/Berlin
                )
        )

    [2] => Array
        (
            [id] => 2
            [artist] => Array
                     (
                         [id] => 14

                         [...]

                      )
            [title] => Some other nice album
            [digital_release] => DateTime Object
                (
                    [date] => 2014-02-01 00:00:00.000000
                    [timezone_type] => 3
                    [timezone] => Europe/Berlin
                )

            [physical_release] => DateTime Object
                (
                    [date] => 2014-02-01 00:00:00.000000
                    [timezone_type] => 3
                    [timezone] => Europe/Berlin
                )

        )

    [...]
)

Maybe someone has a helpful advice how to get such an output :)

I've already tried to use AbstractQuery::HYDRATE_SCALAR, which comes a bit closer to what i want, but the result of the joined table is not a sub-array of the joined column.

Thanks in advance for every helpful hint :)

regards


Solution

  • Your problem is your mapping it's not good i think you have not a relation between Release and Artist.

    You have to set a relation OneToMany between Artist and Release. Like this :

    On artist side
    /**
     * @ORM\OneToMany(targetEntity="Release\Entity\Release", mappedBy="artist")
     */
    protected $release;
    
    And Release side
    /**
     * @ORM\ManyToOne(targetEntity="Artist\Entity\Artist", inversedBy="release")
     * @ORM\JoinColumn(name="artist", referencedColumnName="idArtist", onDelete="RESTRICT")
     */
    protected $artist;
    

    Don't forget to the Artist side to instanciate a collection like this :

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

    with this use :

    use Doctrine\Common\Collections\ArrayCollection;
    

    With this you can set your release entity and set the artist whith an artist object. You'll have the result you want.

    $release = new Release;
    $artist = new Artist;
    $release->setArtist($artist);
    

    EDIT When you have your data, you have to populate your entity with it.

    If you have a Form, you have to use DoctrineHydrator to achieve this, your object will be correctly hydrated, from your form. This may need to dig into doctrine documentation ;) If your data comes from anywhere else, you can populate your entity with your entity's setters.

    For e.g :

    $relaseEntity->setDate($data['date']); // Be carefull to Datetime object for this
    $releaseEntity->setName($data['name']);
    

    And goes on...