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,
];
}
}