Skip to content

[12.x] Fillable BelongsTo relation on Models #55643

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

Closed

Conversation

DarkGhostHunter
Copy link
Contributor

@DarkGhostHunter DarkGhostHunter commented May 4, 2025

What?

This PR allows the developer to fill a BelongsTo relationship through the fill() method, instantly creating the related model if necessary. It only needs a Model instance¹, and setting $fillBelongsToRelations static property to true on the model class.

use Illuminate\Database\Eloquent\Model;

class Car extends Model
{
    protected $fillable = ['name', 'driver', 'features'];
    
    public function driver()
    {
        return $this->belongsTo(Driver::class);
    }
    
    public function features()
    {
        return $this->belongsToMany(Feature::class);
    }
}

$driver = Driver::find(1);

// Before
$car = Car::make([
    'name' => 'Lamborghini Aventador'
])

$car->driver()->associate($driver);

$car->save();

// After
Car::$fillBelongsToRelations = true;

$car = Car::create([
    'name' => 'Lamborghini Aventador',
    'driver' => $driver,
])

How?

When the attribute is filled, it will check if the key is fillable as part of the Model fillable attributes. If it is, and the relation's name matches, it will associate the model into the relation. If the model doesn't exists in the database, it will save it before associating it.

¹: The value must be a Model instance. This way it can be associated with MorphTo relations.

If the value is not a Model, then it will be understood it's a normal attribute and fill it like always.

This is opt-in, meaning, it has to be explicitly enabled by the developer (at least on this version), but with the hope that developers who require to create related records can do it in less lines and less complexity.

@fragkp
Copy link
Contributor

fragkp commented May 4, 2025

Personally, I would be against this PR - even I like the idea/shortcut.

From your example:
driver is not the actual table column. As far as I know, there is no such "magic" like this for the create method in Eloquent at all.

So, maybe it would be better to convert models to the primary key of the BelongsTo columns (driver_id), but that would be a huge change from the current behaviour.

$car = Car::create([
    'name' => 'Lamborghini Aventador',
    'driver_id' => $driver,
])

@shaedrich
Copy link
Contributor

shaedrich commented May 4, 2025

Maybe, this can be made a little more transparent by changing driver to driver.*. This could later allow to only fill specific relation columns like driver.name or even further BelongsTo relations like driver.license.number 🤔 This also would eliminate the need for a isBelongsToRelation method that then could be replace by a simple str_contains($key, '.') check

* @param \Illuminate\Database\Eloquent\Model $instance
* @return $this
*/
protected function fillBelongsToRelation($relation, $instance)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this method is necessary. Are we expecting a user to override this?

The implementation is already very declarative, even if inlined on the call site.

@rodrigopedra
Copy link
Contributor

Couldn't this be a custom cast?

Besides being opt-in, it would be more explicit than a developer remembering to match all criteria (add to $fillable, assigning a model)

Also, as a custom cast, one could check if they are assigning a model or a value, and act accordingly, either by associating the model, or by just assigning the value to the foreign key attribute.

@DarkGhostHunter
Copy link
Contributor Author

DarkGhostHunter commented May 4, 2025

Instead of a custom cast it could be just a Model::$enableFillingBelongsToRelation to enable the behavior, or an $fillableRelations much like $fillable.

The idea is to make filling up a BelongsTo relation as easier as possible. While in the example driver is not a column, it's the relation name and I highly doubt the developer would have the same relation name and column.

I guess the problem here is more about how to communicate to the developer that the attribute to fill is not a column but a relation, and how it can fill it effortlessly in one sentence.

The driver.* approach could be an alternative, especially if the model itself doesn't exist yet. For example, creating a Car with its Driver in one go, the latter using an array, or the Key if it's a Model instance if it already exists.

@DarkGhostHunter DarkGhostHunter force-pushed the feat/fill-with-relations branch from 03e7fd9 to ac9f10b Compare May 5, 2025 00:22
@DarkGhostHunter DarkGhostHunter changed the title [12.x] Adds set relation attribute using Model. [12.x] Fillable BelongsTo relation on Models May 5, 2025
@DarkGhostHunter
Copy link
Contributor Author

Okay, made a rework so it will only work on fill().

An alternative would have been to create new methods for make(), create() and fill() with all their variations, but I considered it to be too bothersome. I mean: makeWithRelations(), forceMakeWithRelations(), createWithRelations(), forceCreateWithRelations(), fillWithRelations(), forceFillWithRealtions().

Would have that been more better than this approach? Let me now.

@taylorotwell
Copy link
Member

Thanks for your pull request to Laravel!

Unfortunately, I'm going to delay merging this code for now. To preserve our ability to adequately maintain the framework, we need to be very careful regarding the amount of code we include.

If applicable, please consider releasing your code as a package so that the community can still take advantage of your contributions!

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

Successfully merging this pull request may close these issues.

5 participants