laravel / ideas

Issues board used for Laravel internals discussions.
940 stars 28 forks source link

Possibility to change query wheres? Or a new has many with null relation? #960

Closed royduin closed 6 years ago

royduin commented 6 years ago

I've got a "One To Many" relationship where a post has many comments (to keep it inline with the docs: https://laravel.com/docs/5.5/eloquent-relationships#one-to-many). But in my situation the post_id column on the comments table can be null. Which means: the comment is for all posts.

To get this working I thought return $this->hasMany('App\Comment')->orWhereNull('post_id'); would do it. But $this->query->whereNotNull($this->foreignKey); is there in the constraints of that relationship, see: https://github.com/laravel/framework/blob/5.5/src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php#L72 so that's not going that good... I've searched around in the query builder class but there isn't a method to change or delete existing / already defined wheres.

So I've created my own relationship for this with just 1 change:

<?php

namespace App\Extensions\Eloquent;

use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasMany;

class HasManyWithNull extends HasMany
{
    /**
     * Set the base constraints on the relation query.
     *
     * @return void
     */
    public function addConstraints()
    {
        if (static::$constraints) {
            $this->query->where($this->foreignKey, '=', $this->getParentKey());

            $this->query->orWhereNull($this->foreignKey);
        }
    }
}

With a trait I can use in models where I need this relationship:

<?php

namespace App\Extensions\Eloquent;

trait HasCustomRelationships
{
    /**
     * Define a has many relation with null.
     *
     * @param  string  $related
     * @param  string  $foreignKey
     * @param  string  $localKey
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function hasManyWithNull($related, $foreignKey = null, $localKey = null)
    {
        $instance = $this->newRelatedInstance($related);

        $foreignKey = $foreignKey ?: $this->getForeignKey();

        $localKey = $localKey ?: $this->getKeyName();

        return new HasManyWithNull(
            $instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey
        );
    }
}

I'd like to see this relationship, the possibility to change the relationship constraints or the possibility to change existing / previously defined wheres in Laravel. Any thoughts on this? Am I the only one who would like to see this or what option is preferable?

phroggyy commented 6 years ago

That’s not a relationship. If it should be displayed everywhere you might as well query for it separately. Your other option is a belongsToMany and manually linking in the pivot table. You could also combine two queries.

TL;DR: if it doesn’t belong to any specific post, it shouldn’t be returned as part of a relation.