1

For my project, i use pommbundle for my Symfony project and i have a question:

For example :

    TABLE Catalogue:
    id = 1
    name = test
    webinfo = 2
    TABLE Info :
    id=1
    webid = 2
    textinfo = OK
    textmore = it's test

Relation

    Catalogue.webinfo = Info.webid

If my entity has a relation with another entity define in a schema and i want a object contains all informations, How to access informations because the dump "Catalogue" returns only an integer for "webinfo".Do I have to create my own method? The bundle contains a method?

Is it possible to show a basic example, for use in symfony2?

Thanks you

2 Answers2

1

Pomm is not an ORM and as such does not provide a method to automagically fetch relations.

This choice is made because of a simple state of most programmers habits:

$blog_posts = $orm->getBlogPosts(); // SELECT * FROM blog.post;

foreach ($blog_posts as $blog_post) {
    printf(
        "The post titled '%s' has been written by '%s'.\n",
        $post->getTitle(),
        $post->getAuthor()->getName() // SELECT * FROM blog.author WHERE author_id = $*
    );
}

The code above emits as many queries in the author table as there are blog posts, this is called a nested loop query and this is a huge performance problem as annoying as hidden because most programmers do not actually see it performs that many queries.

You can do the same in SQL with just one query:

SELECT
  title,
  published_at,
  … -- other fields
  a AS author
FROM
  blog.post p
  LEFT JOIN blog.author a USING (author_id)

This will output lines like:

       title        |      published_at      |  …  |                    author
Gone with the wind  |  2010-04-04 13:43:02…  |  …  | ("Jules Vernes","jules.vernes@gmail.com", … )

Postgres can return a Author row directly in the set of results of the blog posts, this is by far more efficient than the previous solution but this can still cause performance problems if author records are big. It may be interesting to voluntarily limit the amount of author fields fetched in the blog post entities, maybe just the name is enough. Let’s perform such query in the blog post model:

class PostModel extends Model
{
    // …

    public function getWithAuthor()
    {
        // HINT: this is bad practice, read below.
        $sql = <<<SQL
select P.post_id, P.title, …, A.name AS author_name
from blog.post P
    left join blog.author A using (author_id)
SQL;

        return $this->query($sql);
    }

In the controller, it becomes easy to fetch blog posts with the additional author information:

$blog_posts = $this->get('pomm')['my_session']
    ->getModel(PostModel::class)
    ->getWithAuthor();

foreach ($blog_posts as $post) {
    printf(
        "Post '%s' has been written by '%s'.\n",
        $post['title'],
        $post['author_name']
    );
}

But this method is not very portable since the name of each relation (table) is hardcoded and so is the projection (the fields in the SELECT part). Changing the database structure would ruin this query. Here is a way to circumvent that:

class PostModel extends Model
{
    // …

    public function getWithAuthor(Where $condition = null)
    {
        $condition = (new Where)->andWhere($condition); // empty condition and empty condition => true.
        $sql = <<<SQL
select {projection}
from   {post} P
  left join {author} A using (author_id)
where {condition}
SQL;

        $projection = $this->createProjection()           // use Post fields
            ->setField('author_name', 'A.name', 'text');  // add a new field with its type
        $author_relation = $this->getSession()
            ->getModel(AuthorModel::class)                // get author relation name
            ->getStructure()
            ->getRelation();

        $sql = strtr(                                     // transform the SQL
            $sql,
            [
                '{projection}'      => $projection->formatFieldsWithFieldAlias("P"),
                '{post}'            => $this->structure->getRelation(),
                '{author}'          => $author_relation,
                '{condition}'       => $condition,
            ]);

        return $this->query($sql, $condition->getValues(), $projection);
    }

What if we need to fetch all the blog posts since yesterday with their author name?

$blog_posts = $this->get('pomm')['my_session']
    ->getModel(PostModel::class)
    ->getWithAuthor(new Where("published_at > $*::timestamptz", [new \DateTime('yesterday')]));

It is also possible to fetch back a whole nested Author instance, just change the projection in the method:

$projection = $this->createProjection()
    ->setField('author', 'A', 'blog.author');

NOTE: the type of the field above is the table where the row originates. Creating a table in Postgres means creating a type.

In the controller:

foreach ($blog_posts as $post) {
    printf(
        "Post '%s' has been written by '%s'.\n",
        $post['title'],
        $post['author']['name']
    );
}

Postgres is the ORM.

greg
  • 3,354
  • 1
  • 24
  • 35
0

Thanks you for all, I have just a second error in my code The correction :

public function findWithJointureAll(Where $condition = null)
{
    $famille_model = $this
        ->getSession()
        ->getModel('\AppBundle\Entity\MyDb1\PublicSchema\FamilleModel')
        ;

    $condition = (new Where)->andWhere($condition);

    $sql ="
    select
        {projection}
    from
        {ssfamille} ssf
        inner join {famille} fam ON ssf.\"Famille\" = fam.\"ID\" where {condition}";

    $projection = $this->createProjection()
        ->setField('test', 'fam','"Famille"')
        ;

    $sql = strtr(
        $sql,
        [
            '{ssfamille}'    => $this->structure->getRelation(),
            '{famille}'      => $famille_model->getStructure()->getRelation(),
            '{projection}'   => $projection->formatFieldsWithFieldAlias('ssf'),
            '{condition}'    => $condition,
        ]
    );

    return $this->query($sql,$condition->getValues(),$projection);
}

I want just add $projection->formatFieldsWithFieldAlias('ssf') et quote because name base with upper case...

Thanks you for your bundle it's just perfect!!