Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hasManyToMany doesn't work as expected #938

Closed
viktoras25 opened this issue Jul 26, 2013 · 21 comments
Closed

hasManyToMany doesn't work as expected #938

viktoras25 opened this issue Jul 26, 2013 · 21 comments

Comments

@viktoras25
Copy link
Contributor

I'm trying to create a little test blog application. What I would like to achieve is:
Posts(m)---PostsTags---Tags(n)
Ordinary m-n relation, for which Model::hasManyToMany can be utilized.

I followed reference examples and ended up with this:
https://gist.github.com/charnad/b144d2b7664d4337f04f

I get: tag_id is required

If I save each tag separately before saving post, or use existing tags - only one PostTags entry is saved, for the last of the tags in the array.

If later I want to save a new array of tags instead of the old ones, old tags are not replaced, but remain and $post->tags return both old and new tags.

Phalcon version 1.2.0

@viktoras25
Copy link
Contributor Author

https://github.com/charnad/phalcon-test

I've set up a repository to illustrate my issue.
I expect, that Post will be saved along with 2 Tags, and 2 PostsTags will be created (post-tag1, post-tag2). Unfortunately this doesn't happen.
Tried on Linux with 1.2.0 and on Windows with 1.2.1

@p0rsche
Copy link

p0rsche commented Jul 29, 2013

Confirmed on Linux w/phalcon 1.2.1, x64

@ghost
Copy link

ghost commented Jul 30, 2013

Looking into this.

@ghost
Copy link

ghost commented Jul 31, 2013

SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END FROM sqlite_master WHERE type='table' AND tbl_name='posts'
PRAGMA table_info('posts')
INSERT INTO "posts" ("title", "content", "id") VALUES (?, ?, null)
SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END FROM sqlite_master WHERE type='table' AND tbl_name='posts_tags'
PRAGMA table_info('posts_tags')
SELECT COUNT(*) "rowcount" FROM "posts_tags" WHERE "post_id" = ? AND "tag_id" = ?
INSERT INTO "posts_tags" ("post_id", "tag_id") VALUES (?, null)

Looks like insertion into posts_tags happens too early — tags table needs to be updated first.

@ghost
Copy link

ghost commented Jul 31, 2013

    $di->set('db', function() {
        $em = new \Phalcon\Events\Manager();
        $em->attach('db',
            function($event, $conn)
            {
                if ($event->getType() == 'beforeQuery') {
                    echo $conn->getSQLStatement(), "\n";
                }
            }
        );

        $conn = new \Phalcon\Db\Adapter\Pdo\Sqlite(array("dbname" => __DIR__ . "/phalcon.sqlite"));
        $conn->getInternalHandler()->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
        $conn->setEventsManager($em);
        return $conn;
    });
INSERT INTO "posts" ("title", "content", "id") VALUES (?, ?, null)
SELECT COUNT(*) "rowcount" FROM "posts_tags" WHERE "post_id" = ? AND "tag_id" = ?
INSERT INTO "posts_tags" ("post_id", "tag_id") VALUES (?, null)
INSERT INTO "tags" ("tag", "id") VALUES (?, null)

@ghost ghost mentioned this issue Jul 31, 2013
@harwoodr
Copy link

harwoodr commented Aug 3, 2013

Tried 1.3.0 branch with fix #938 and it doesn't seem to fix the problem.

@ghost
Copy link

ghost commented Aug 3, 2013

Do you have a test case?

@harwoodr
Copy link

harwoodr commented Aug 4, 2013

I used both versions of phalcon the one from above and your fork with sql lite... it doesn't give the error - but it doesn't insert anything into posts_tags either.

@ghost
Copy link

ghost commented Aug 4, 2013

Did you use my fork of Phalcon or my fork of https://github.com/charnad/phalcon-test?

The first one works, the second one does not as I fixed the C code, not PHP one.

@viktoras25
Copy link
Contributor Author

Do I get it right, that plain

$tag = new Tag();
$post->tags = array($tag);
$post->save();

won't work without magic Model class?

You said

I fixed the C code, not PHP one

but what is wrong with my code? It is almost identical to "official" examples.

@ghost
Copy link

ghost commented Aug 4, 2013

@charnad Please compile and install the latest 1.2.2 and your code will work.

See this test case: https://github.com/phalcon/cphalcon/blob/1.2.2/unit-tests/ModelsRelationsTest.php#L324

@viktoras25
Copy link
Contributor Author

Oh, I see. Misunderstood your comment.
Thank you, will try it now.

@viktoras25
Copy link
Contributor Author

In 1.2.2 creating and updating entries works fine! Thank you.

However one question left unclear. If I want to replace old relations with new ones, what should I do?
If I say have:

$post->tags = array($tag1);
$post->save();
$post->tags = array($tag2);
$post->save();

I will have both tags saved (which is good) and both related to this post (which is not good).

@ghost
Copy link

ghost commented Aug 6, 2013

I would delete them first explicitly

@phalcon What do you think?

@phalcon
Copy link
Collaborator

phalcon commented Aug 6, 2013

Well, I don't know if the ORM must implicitly delete them and replace with the new ones, that might not be obvious to many programmers deleting data permanently when the expected behavior may be another, ie. add more tags instead of replace them.

What about delete them manually?

$post->tags->delete();
$post->tags = array($tag1);
$post->save();

@viktoras25
Copy link
Contributor Author

I wouldn't like to delete tags themselves, they might be in use with other posts. I just want to remove entries from the model in the middle (PostsTags).

$post->tags->delete();

This will delete tags also, right?

So do I need to define another relation just for PostsTags so that I could remove them?

@phalcon
Copy link
Collaborator

phalcon commented Aug 6, 2013

Sorry, I meant:

$post->postsTags->delete();

My concern is about whether this behavior is the obvious behavior, replace vs. append. What a developer is expecting without read the docs, I think "append" is a safe behavior, something you can figure out without consequences.

@viktoras25
Copy link
Contributor Author

I understand your point, safer way is almost always better. Can I then please have an option for setting an alias for the model in the middle?

Currently manyToMany is set:

$this->hasManyToMany(
    "id", 
    "Blog\\Model\\PostsTags", 
    "post_id", 
    "tag_id", 
    "Blog\\Model\\Tag", 
    "id", 
    array("alias" => "tags")
);

I would like to have:

-    array("alias" => "tags")
+    array("alias" => "tags", "relationAlias" => "postsTags")

@phalcon
Copy link
Collaborator

phalcon commented Aug 8, 2013

You can set a hasMany relation to PostsTags:

$this->hasManyToMany(
    "id", 
    "Blog\\Model\\PostsTags", 
    "post_id",     
    array("alias" => "postsTags")
);

@viktoras25
Copy link
Contributor Author

Ok then. Thank you, I got the answers, so I close the issue.

@bendo01
Copy link

bendo01 commented Nov 3, 2013

using PhalconPHP 1.3 , testing hasManyToMany relation Groups(m) <---- GroupsMenus --- > Menus(n)

when editing, in relation table GroupsMenus create duplicate data, and also if we create our own behavior, eventType in behavior doesn't fire up

| id | group_id | menu_id |

| 1 | 1 | 1 | <---- add new data
| 2 | 1 | 1 | <---- result after edit data 1

here is my gist
https://gist.github.com/bendo01/7290511

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants