silverqx / TinyORM

Modern C++ ORM library
https://www.tinyorm.org
MIT License
247 stars 25 forks source link

Eager loading with SoftDeletes (inside with() lambda) #56

Open shakhinn opened 23 hours ago

shakhinn commented 23 hours ago

Hi

Is there any way i can load relation with trashed items in with method?

image

silverqx commented 23 hours ago

Do you mean this?

auto user = User::withTrashed()->with(...).get()

or something like this should be possible too, but it's doing something different than above and the following it's not eager load:

user->posts()->onlyTrashed().get();
user->posts()->withTrashed().get();
shakhinn commented 23 hours ago

I want something like this

user->posts()->withTrashed().get();

But I have ModelsCollection and user->posts()->withTrashed() send query for every user. I want to avoid this by using with method

I haven't found how i can do this.

P.S. It is awful hack, but now i have written

auto user = User::withTrashed()->with({{"posts", [](auto& query){query.whereRaw(R"(1 = 1 --)")}}}).get()

this sql injection helps me to disable where posts.deleted_at is null that generates by default

shakhinn commented 20 hours ago

UPD this hack does't work :( ORM ignores deleted_at column and returns invalid QVariant when i call trashed() method

silverqx commented 18 hours ago

So you have ModelsCollection of Users and you need to select all Posts that are not trashed eagerly.

You have two choices, this is what I came up with fastly:

auto posts = Post::withTrashed()->whereIn("user_id", users.modelKeys()).get();

or

auto users = User::with({{"posts", [](auto &query)
{
    query.whereNotNull("deleted_at");
}}})->get();
shakhinn commented 18 hours ago

Thanks!

silverqx commented 17 hours ago

Another solution would be to define the relation like:

std::unique_ptr<HasMany<User, Post>>
postsTrashed()
{
    auto relation = hasMany<Post>(u"post_id"_s);
    relation->withTrashed();
    return relation;
}
silverqx commented 2 hours ago

The real problem here is that you should be able to call it like this:

auto users = User::with({{"posts", [](auto &query)
{
    query.withTrashed();
}}})->get();

But this is currently not possible because of this, the relation reference should be passed into this lambda (eg. in this case HasMany<Post> &), but it was tough to do in c++, so I'm passing the basic query builder only QueryBuilder &, so you don't have access to many ORM related methods inside these lambdas.

And if not a relation reference then at least TinyBuilder<Post> & instead of QueryBuilder &, but I think there would be a big difference from a refactor perspective if it will be a relation or TinyBuilder reference.

It would be a valuable refactor. Maybe I should try fixing it. It's not a big deal though. For example, in this case, you can write the thing manually, like query.whereNotNull("deleted_at") instead of query.withTrashed().

silverqx commented 2 hours ago

I leave it open as it can be enhanced in the future.