Skip to content
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

Feat nested relations #364

Merged
merged 2 commits into from
Nov 16, 2021
Merged

Conversation

kishieel
Copy link
Contributor

@kishieel kishieel commented Nov 11, 2021

Overview

Allows for factoring nested relations with dot notation. Supports HasMany and BelongsToMany relations type. In case of custom data in pivot table you should create helper function in your factory which must be names as pivot{name of relationship method}.

Example use case:

employees <-- one to many --> places <-- many to many --> tasks.

Each employee has assigned many places of work and each places belongs to many tasks. Pivot table between places and tasks has additional column with salary. Resource returns those relations with information about salary from pivot.

Models

Employee

class Employee extends Model
{
    use HasFactory;

    public function places(): HasMany
    {
        return $this->hasMany(Place::class);
    }
}

Place

class Place extends Model
{
    use HasFactory;

    public function tasks(): BelongsToMany
    {
        return $this->belongsToMany(Task::class);
    }
}

Task

class Task extends Model
{
    use HasFactory;

    public function places(): BelongsToMany
    {
        return $this->belongsToMany(Place::class);
    }
}

Factories

EmployeeFactory

class EmployeeFactory extends Factory
{
    protected $model = Employee::class;

    public function definition(): array
    {
        return [
           'name' => $this->faker->name(),
        ];
    }
}

PlaceFactory

class PlaceFactory extends Factory
{
    protected $model = Place::class;

    public function definition(): array
    {
        return [
           'location' => $this->faker->word(),
        ];
    }

    public function pivotTasks(): array
    {
        return [
            'salary' => $this->faker->randomNumber(),
        ];
    }
}

TaskFactory

class TaskFactory extends Factory
{
    protected $model = Task::class;

    public function definition(): array
    {
        return [
           'description' => $this->faker->word(),
        ];
    }

    public function pivotPlaces(): array
    {
        return [
            'salary' => $this->faker->randomNumber(),
        ];
    }
}

Resources

EmployeeResource

class EmployeeResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'places' => PlaceResource::collection($this->places)
        ];
    }
}

PlaceResource

class PlaceResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'tasks' => $this->tasks->map(function (Task $task) {
                return [
                    'task_id' => $task->id,
                    'salary' => $task->pivot->salary
                ];
            })
        ];
    }
}

Controllers

EmployeeController

/**
 * @group Employee
 * @authenticated
 */
class EmployeeController extends Controller
{
    /**
     * Display a listing of the employees.
     *
     * @apiResourceCollection App\Http\Resources\EmployeeResource
     * @apiResourceModel App\Models\Escort paginate=10 with=places.tasks
     *
     * @param Request $request
     * @return AnonymousResourceCollection
     */
    public function index(Request $request): AnonymousResourceCollection
    {
        $perPage = $request->input('per-page', 10);
        $employees = Employee::paginate($perPage);

        return EmployeeResource::collection($employees);
    }
}

Result

The result of current setup will be as follow:

{
    "data": [
        {
            "id": 1,
            "name": "Adam",
            "places": [
                {
                    "location": "Warszawa",
                    "tasks": [
                        {
                            "task_id": 1,
                            "salary": 128
                        }
                    ]
                }
            ]
        },
        {
            "id": 2,
            "name": "Romek",
            "places": [
                {
                    "location": "Kraków",
                    "tasks": [
                        {
                            "task_id": 2,
                            "salary": 48392
                        }
                    ]
                }
            ]
        }
    ],
    "links": {
            
    },
    "meta": {
    
    }
}

@kishieel kishieel force-pushed the feat/nested_relations branch from 802c53d to 1c4e9f9 Compare November 11, 2021 22:58
@shalvah
Copy link
Contributor

shalvah commented Nov 16, 2021

Well, this seems like a lot of well-thought-out work (even though I don't fully understand it). Well done!

@shalvah shalvah merged commit 05e0f21 into knuckleswtf:master Nov 16, 2021
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.

2 participants