Home > Blockchain >  How to establish relationship in Laravel, to store id and retrieve as object
How to establish relationship in Laravel, to store id and retrieve as object

Time:01-23

I'm building a CRUD using Laravel and Angular technologies. However, as I am new to Laravel technology, I am not able to establish a relationship between two entities. I have Group entity and Subgroup entity. A group has only its id and its name as attributes. A subgroup has its id, name and also a group id.

I would like to know how can I establish a relationship between group and subgroup. I want to store the id of a group in a subgroup record, but when performing queries with the HTTP verb I want the Group object to be returned and not its id, so that I can manipulate this object in the front-end.

Model Group:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Group extends Model
{
    use HasFactory;
    protected $fillable = [
        'name'
    ];

    protected $table = 'groups';
}

Group Resource:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class GroupResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}

Group Migration:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('groups', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('groups');
    }
};

Subgroup Resource:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class SubgroupResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'group_id' => $this->group_id,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}

Subgroup Model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Subgroup extends Model
{
    use HasFactory;
    protected $fillable = [
        'group_id'
    ];

    protected $table = 'subgroups';
}

Subgroup Migration:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('subgroups', function (Blueprint $table) {
            $table->increments('id');
            $table->foreignIdFor(Group::class);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('subgroups');
    }
};

Subgroup Controller:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Subgroup;

class SubgroupController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return Subgroup::all();
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        return Subgroup::create($request->all());
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        return Subgroup::find($id);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        if (Subgroup::where('id', $id)->exists()) {
            $subgroup = Subgroup::find($id);
            $subgroup->group_id = $request->group_id;

            $subgroup->save();
            return response()->json([
                "message" => "Success"
            ], 200);
        } else {
            return response()->json([
                "message" => "Error"
            ], 404);
        }
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        if (Subgroup::where('id', $id)->exists()) {
            $subgroup = Subgroup::find($id);
            $subgroup->delete();

            return response()->json([
                "message" => "Successfully deleted"
            ], 202);
        } else {
            return response()->json([
                "message" => "Not Found"
            ], 404);
        }
    }
}

I would like to know if I'm on the right path of establishing a relationship between the tables. Also, when retrieving a Subgroup I would like the group_id field not to return the id but a group object, how can I do that?

CodePudding user response:

You have to use Relationships, it is a whole section on the documentation...

So, I think you want a 1-to-N (one to many), so one Group can have multiple Subgroups.

If that is the case, you should have:

Group model

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Group extends Model
{
    use HasFactory;

    protected $fillable = [
        'name'
    ];

    public function subgroups()
    {
        return $this->hasMany(Subgroup::class);
    }
}

Subgroup

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Subgroup extends Model
{
    use HasFactory;

    protected $fillable = [
        'group_id'
    ];

    public function group()
    {
        return $this->belongsTo(Group::class);
    }
}

See that I have removed $table from both, as the framework will be able to define the same name as you defined there.

Now, if you want to return a name instead of a group_id on your Resource, you should have it like this (after applying the previous changes):

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class SubgroupResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'group_name' => $this->group->name,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}

If you read the documentation, you will see that, after defining a relationship (for example, group method), you can use $model->group and that will return the associated Group model if found, else null. You can also use $model->group() and that will return a Builder so you can do something on that resource (with a builder, not a model class).


In your specific case, you can directly return the object, as you wanted, by doing:

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class SubgroupResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'group' => $this->group,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}
  • Related