Skip to content

feat: bypass __set() for loaded relation assignment#27

Merged
michalsn merged 3 commits intodevelopfrom
feat/improve-relation-assignment
Mar 11, 2026
Merged

feat: bypass __set() for loaded relation assignment#27
michalsn merged 3 commits intodevelopfrom
feat/improve-relation-assignment

Conversation

@michalsn
Copy link
Owner

Description
This PR changes how loaded relations are assigned to returned entities.

Closes #26

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

@michalsn michalsn added the enhancement New feature or request label Mar 10, 2026
@neznaika0
Copy link
Contributor

I installed nested-models, added your patch, added Tag::posts, added initialization. I see the query in the logs.

INFO - 2026-03-11 00:04:46 --> SELECT *
FROM `tags`
WHERE `tags`.`id` = 1
INFO - 2026-03-11 00:04:46 --> SELECT `posts`.*, `post_tag`.`tag_id`
FROM `posts`
LEFT JOIN `post_tag` ON `post_tag`.`post_id` = `posts`.`id`
WHERE `post_tag`.`tag_id` IN (1)

Tests, ok:

    public function testFindWithTagsAndStrictProperties(): void
    {
        $post = model(PostModel::class)->with('tags')->find(1);

        self::assertInstanceOf(Post::class, $post);
        self::assertNotEmpty($post->tags);

        $tag = array_first($post->tags);

        self::assertInstanceOf(Tag::class, $tag);
    }

    public function testFindWithPostsAndStrictProperties(): void
    {
        $tag = model(TagModel::class)->with('posts')->find(1);

        self::assertInstanceOf(Tag::class, $tag);

        $post = array_first($tag->posts);

        self::assertInstanceOf(Post::class, $post);
    }

I didn't check lazy load because it needs new tests. It seems you are in the right direction.

@michalsn
Copy link
Owner Author

Thanks.

@michalsn michalsn merged commit 203fcea into develop Mar 11, 2026
13 of 15 checks passed
@neznaika0
Copy link
Contributor

@michalsn Have you evaluated the strict object? Can this be added to the framework? As a separate class, it doesn't look very nice, but if you change the default and add strict mode...?

@neznaika0
Copy link
Contributor

I was also able to get nested relationships.

$tag = model(TagModel::class)->with('posts')->with('posts.user')->find(1);

I'm not sure if this is correct, but since we only allow properties from $attributes, then checking arrray_key_exists() (allowed NULL) for lazy loading breaks the process.

By default, you need to add

    protected $attributes = [
        'id'           => null,
        // ...
        'tags'         => null,
        'user'         => null,
    ];

and then check isset(). This may or may not break the $handledRelations query history...

    public function __get(string $key)
    {
        if (isset($this->attributes[$key])) {
            return parent::__get($key);
        }

        $model = $this->getRelationModel();

        if ($model !== null && method_exists($model, $key)) {
            if (! isset($this->handledRelations[$key]) && ! isset($this->attributes[$key])) {
                $this->handleRelation($key, $model);
                $this->handledRelations[$key] = true;
            }

            return $this->attributes[$key] ?? null;
        }

        return parent::__get($key);
    }

@michalsn
Copy link
Owner Author

I don’t think relations should be declared in $attributes. Those keys represent real entity fields, while relations are a transient loaded state.

Have you evaluated the strict object? Can this be added to the framework?

Not sure if there is any demand for this. Also, there will be some limitations, like primary key mapping, which won't be allowed, because it will conflict with the model.

@neznaika0
Copy link
Contributor

Not always. We can create our own collections if we plan to maintain relationships. Or when changing the Post, manipulate the Tags.

I understand your concern and the narrow focus of my preference. No - means no. I'll invent something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feat: Use mapped attributes as property object

2 participants