flyingL123
flyingL123

Reputation: 8106

Laravel save one to many relationship

I have the following relationships set up in Laravel:

OrderStatus Model
  - hasMany('Order')

 Order Model
  - 'belongsTo('OrderStatus');

The database is set up with an orders table and an order_statuses table. The orders table has a field for order_status_id.

When I save an Order, I manually set the order_status_id by fetching the appropriate Order Status model, like this:

$status = OrderStatus::where(['name'=>'sample_status'])->firstOrFail();
$order->order_status_id = $status->id;
$order->save();

I'm wondering if there is a built in function to do this rather than setting the order_status_id manually. I've read about "Attaching a related model", and "Associating Models" in the Laravel docs, but I can't figure out if these fit my use case. I think the issue I'm having is that I'm working directly with the child model (the order), and trying to set it's parent. Is there a function for this?

Upvotes: 17

Views: 51152

Answers (3)

Mahesh Yadav
Mahesh Yadav

Reputation: 2684

You can go with a custom solution.

I am explaining an example, just coded and very much similar to your question, hope it will help. I have a Question Model and AnswerOption Model as below.

Question Model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Question extends Model
{
    protected $table = 'questions';
    protected $fillable =  [
        'title',
        'created_at',
        'updated_at'            
    ];


    /**
     * Get the answer options for the question.
     */
    public function answerOptions()
    {
        return $this->hasMany('App\Models\AnswerOption');
    }


    /**
     * @param array $answerOptions
     */
    public function syncAnswerOptions(array $answerOptions)
    {
        $children = $this->answerOptions;
        $answerOptions = collect($answerOptions);        
        $deleted_ids = $children->filter(
            function ($child) use ($answerOptions) {
                return empty(
                    $answerOptions->where('id', $child->id)->first()
                );
            }
        )->map(function ($child) {
                $id = $child->id;
                $child->delete();                
                return $id;
            }
        );        
        $attachments = $answerOptions->filter(
            function ($answerOption) {
                 // Old entry (you can add your custom code here)
                return empty($answerOption['id']);
            }
        )->map(function ($answerOption) use ($deleted_ids) {
                // New entry (you can add your custom code here)           
                $answerOption['id'] = $deleted_ids->pop();                
                return new AnswerOption($answerOption);
        });        
        $this->answerOptions()->saveMany($attachments);
    }   
}

AnswerOption Model

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class AnswerOption extends Model
{
    protected $table = 'answer_options';

    protected $fillable =  [
        'question_id',
        'title',
        'ord',
        'created_at',
        'updated_at'            
    ];      

    /**
     * Get the question that owns the answer.
     */
    public function question()
    {
        return $this->belongsTo('App\Models\Question');
    }   
}

Here you can see a single question hasMany answer options, you can see I have used BelongsTo , hasMany relationsip in models.

Now in QuestionController during Question save and update, you can also save the answer options.

For this I have written syncAnswerOptions method in Question Model.

You just need to pass the array of options with id, if id is present already in the database then it will update, if id is blank it will add a new record, If Id was there but not in your new array, then that record will get deleted.

/**
 * If you are attaching AnswerOption(s) for the first time, then pass
 * in just the array of attributes:
 * [
 *     [
 *         // answer option attributes...
 *     ],
 *     [
 *         // answer option attributes...
 *     ],
 * ]
 *//**
 * If you are attaching new AnswerOption(s) along with existing
 * options, then you need to pass the `id` attribute as well.
 * [
 *     [
 *         'id' => 24
 *     ],
 *     [
 *         // new answer option attributes...
 *     ],
 * ]
 */

In Question controller's store and update method call this method just after question add and update call.

$question->syncAnswerOptions($data['answerOptions']);

$data['answerOptions'] is Array of answer options just like described in the comments.

Upvotes: 0

Mysteryos
Mysteryos

Reputation: 5791

The correct way, to save a relationship for a new related model is as follows:

$status = OrderStatus::where(['name'=>'sample_status'])->firstOrFail();
$order = new Order;
$status->order()->save($order);

Documentation link : http://laravel.com/docs/4.2/eloquent#inserting-related-models

Upvotes: 12

lukasgeiter
lukasgeiter

Reputation: 153150

Sure you can do this:

$status = OrderStatus::where(['name'=>'sample_status'])->firstOrFail();
$order = new Order;
$order->status()->associate($status);
$order->save();

(status() is the belongsTo relation. You might need to adjust that name)

Upvotes: 29

Related Questions