Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use Binaryk\LaravelRestify\Http\Controllers\GlobalSearchController;
use Binaryk\LaravelRestify\Http\Controllers\RepositoryAttachController;
use Binaryk\LaravelRestify\Http\Controllers\RepositoryDestroyController;
use Binaryk\LaravelRestify\Http\Controllers\RepositoryFilterController;
use Binaryk\LaravelRestify\Http\Controllers\RepositoryIndexController;
Expand All @@ -22,3 +23,6 @@
Route::patch('/{repository}/{repositoryId}', '\\'.RepositoryUpdateController::class);
Route::put('/{repository}/{repositoryId}', '\\'.RepositoryUpdateController::class);
Route::delete('/{repository}/{repositoryId}', '\\'.RepositoryDestroyController::class);

// Attach related repository id
Route::post('/{repository}/{repositoryId}/attach/{relatedRepository}', '\\'.RepositoryAttachController::class);
64 changes: 64 additions & 0 deletions src/Http/Controllers/RepositoryAttachController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace Binaryk\LaravelRestify\Http\Controllers;

use Binaryk\LaravelRestify\Http\Requests\RepositoryAttachRequest;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use DateTime;
use Illuminate\Support\Arr;

class RepositoryAttachController extends RepositoryController
{
public function __invoke(RepositoryAttachRequest $request)
{
$model = $request->findModelOrFail();
$repository = $request->repository()->allowToUpdate($request);

return $repository->attach(
$request, $request->repositoryId,
collect(Arr::wrap($request->input($request->relatedRepository)))
->map(fn ($relatedRepositoryId) => $this->initializePivot(
$request, $model->{$request->viaRelationship ?? $request->relatedRepository}(), $relatedRepositoryId
))
);
}

/**
* Initialize a fresh pivot model for the relationship.
*
* @param RestifyRequest $request
* @param $relationship
* @return mixed
* @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException
* @throws \Binaryk\LaravelRestify\Exceptions\UnauthorizedException
*/
protected function initializePivot(RestifyRequest $request, $relationship, $relatedKey)
{
$parentKey = $request->repositoryId;

$parentKeyName = $relationship->getParentKeyName();
$relatedKeyName = $relationship->getRelatedKeyName();

if ($parentKeyName !== $request->model()->getKeyName()) {
$parentKey = $request->findModelOrFail()->{$parentKeyName};
}

if ($relatedKeyName !== ($request->newRelatedRepository()::newModel())->getKeyName()) {
$relatedKey = $request->findRelatedModelOrFail()->{$relatedKeyName};
}

($pivot = $relationship->newPivot())->forceFill([
$relationship->getForeignPivotKeyName() => $parentKey,
$relationship->getRelatedPivotKeyName() => $relatedKey,
]);

if ($relationship->withTimestamps) {
$pivot->forceFill([
$relationship->createdAt() => new DateTime,
$relationship->updatedAt() => new DateTime,
]);
}

return $pivot;
}
}
52 changes: 52 additions & 0 deletions src/Http/Requests/InteractWithRepositories.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,41 @@ public function findModelQuery($repositoryId = null, $uriKey = null)
);
}

public function findModelOrFail($id = null)
{
if ($id) {
return $this->findModelQuery($id)->firstOrFail();
}

return once(function () {
return $this->findModelQuery()->firstOrFail();
});
}

public function findRelatedModelOrFail()
{
return once(function () {
return $this->findRelatedQuery()->firstOrFail();
});
}

public function findRelatedQuery($relatedRepository = null, $relatedRepositoryId = null)
{
return $this->repository($relatedRepository ?? request('relatedRepository'))::newModel()
->newQueryWithoutScopes()
->whereKey($relatedRepositoryId ?? request('relatedRepositoryId'));
}

protected function findPivot(RestifyRequest $request, $model)
{
$pivot = $model->{$request->relatedRepository}()->getPivotAccessor();

return $model->{$request->viaRelationship}()
->withoutGlobalScopes()
->lockForUpdate()
->findOrFail($request->relatedRepositoryId)->{$pivot};
}

public function viaParentModel()
{
$parent = $this->repository($this->viaRepository);
Expand All @@ -163,4 +198,21 @@ public function viaQuery()
{
return $this->viaParentModel()->{$this->viaRelationship}();
}

/**
* Get a new instance of the "related" resource being requested.
*
* @return Repository
*/
public function newRelatedRepository()
{
$resource = $this->relatedRepository();

return new $resource($resource::newModel());
}

public function relatedRepository()
{
return Restify::repositoryForKey($this->relatedRepository);
}
}
7 changes: 7 additions & 0 deletions src/Http/Requests/RepositoryAttachRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Binaryk\LaravelRestify\Http\Requests;

class RepositoryAttachRequest extends RestifyRequest
{
}
1 change: 1 addition & 0 deletions src/Http/Requests/RestifyRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Binaryk\LaravelRestify\Http\Requests;

use Binaryk\LaravelRestify\Restify;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\App;

Expand Down
12 changes: 12 additions & 0 deletions src/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,18 @@ public function update(RestifyRequest $request, $repositoryId)
->success();
}

public function attach(RestifyRequest $request, $repositoryId, Collection $pivots)
{
DB::transaction(function () use ($request, $pivots) {
return $pivots->map(fn ($pivot) => $pivot->forceFill($request->except($request->relatedRepository)))
->map(fn ($pivot) => $pivot->save());
});

return $this->response()
->data($pivots)
->created();
}

public function destroy(RestifyRequest $request, $repositoryId)
{
$status = DB::transaction(function () {
Expand Down
2 changes: 0 additions & 2 deletions tests/Controllers/RepositoryFilterControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public function test_can_get_available_filters()
$response = $this
->withoutExceptionHandling()
->getJson('restify-api/posts/filters')
->dump()
->assertStatus(200);

$this->assertCount(3, $response->json('data'));
Expand All @@ -39,7 +38,6 @@ public function test_the_boolean_filter_is_applied()
$response = $this
->withoutExceptionHandling()
->getJson('restify-api/posts?filters='.$filters)
->dump()
->assertStatus(200);

$this->assertCount(1, $response->json('data'));
Expand Down
66 changes: 66 additions & 0 deletions tests/Controllers/RepositoryPivotControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Controllers;

use Binaryk\LaravelRestify\Tests\Fixtures\Company\Company;
use Binaryk\LaravelRestify\Tests\IntegrationTest;

class RepositoryPivotControllerTest extends IntegrationTest
{
public function test_attach_a_user_to_a_company()
{
$user = $this->mockUsers(2)->first();
$company = factory(Company::class)->create();

$response = $this->postJson('restify-api/companies/'.$company->id.'/attach/users', [
'users' => $user->id,
'is_admin' => true,
])
->assertStatus(201);

$response->assertJsonFragment([
'company_id' => '1',
'user_id' => $user->id,
'is_admin' => true,
]);
}

public function test_attach_multiple_users_to_a_company()
{
$user = $this->mockUsers(2)->first();
$company = factory(Company::class)->create();
$usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users');
$this->assertCount(0, $usersFromCompany->json('data'));

$response = $this->postJson('restify-api/companies/'.$company->id.'/attach/users', [
'users' => [1, 2],
'is_admin' => true,
])
->assertStatus(201);

$response->assertJsonFragment([
'company_id' => '1',
'user_id' => $user->id,
'is_admin' => true,
]);

$usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users');
$this->assertCount(2, $usersFromCompany->json('data'));
}

public function test_after_attach_a_user_to_company_number_of_users_increased()
{
$user = $this->mockUsers()->first();
$company = factory(Company::class)->create();

$usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users');
$this->assertCount(0, $usersFromCompany->json('data'));

$this->postJson('restify-api/companies/'.$company->id.'/attach/users', [
'users' => $user->id,
]);

$usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users');
$this->assertCount(1, $usersFromCompany->json('data'));
}
}
21 changes: 21 additions & 0 deletions tests/Factories/CompanyFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

use Binaryk\LaravelRestify\Tests\Fixtures\Company\Company;
use Faker\Generator as Faker;

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/

$factory->define(Company::class, function (Faker $faker) {
return [
'name' => $faker->name,
];
});
23 changes: 23 additions & 0 deletions tests/Fixtures/Company/Company.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Fixtures\Company;

use Binaryk\LaravelRestify\Tests\Fixtures\User\User;
use Illuminate\Database\Eloquent\Model;

class Company extends Model
{
protected $fillable = [
'id',
'name',
];

public function users()
{
return $this->belongsToMany(User::class, 'company_user', 'company_id', 'user_id')
->withPivot([
'is_admin',
])
->withTimestamps();
}
}
10 changes: 10 additions & 0 deletions tests/Fixtures/Company/CompanyRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Fixtures\Company;

use Binaryk\LaravelRestify\Repositories\Repository;

class CompanyRepository extends Repository
{
public static $model = Company::class;
}
8 changes: 8 additions & 0 deletions tests/Fixtures/User/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Binaryk\LaravelRestify\Contracts\RestifySearchable;
use Binaryk\LaravelRestify\Contracts\Sanctumable;
use Binaryk\LaravelRestify\Tests\Fixtures\Company\Company;
use Binaryk\LaravelRestify\Tests\Fixtures\Post\Post;
use Binaryk\LaravelRestify\Traits\InteractWithSearch;
use Illuminate\Contracts\Auth\MustVerifyEmail;
Expand Down Expand Up @@ -81,6 +82,13 @@ public function posts()
return $this->hasMany(Post::class);
}

public function companies()
{
return $this->belongsToMany(Company::class, 'company_user', 'user_id', 'company_id')->withPivot([
'is_admin',
])->withTimestamps();
}

/**
* Set default test values.
*/
Expand Down
2 changes: 2 additions & 0 deletions tests/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Binaryk\LaravelRestify\Exceptions\RestifyHandler;
use Binaryk\LaravelRestify\LaravelRestifyServiceProvider;
use Binaryk\LaravelRestify\Restify;
use Binaryk\LaravelRestify\Tests\Fixtures\Company\CompanyRepository;
use Binaryk\LaravelRestify\Tests\Fixtures\Post\Post;
use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostAuthorizeRepository;
use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostMergeableRepository;
Expand Down Expand Up @@ -178,6 +179,7 @@ public function loadRepositories()
Restify::repositories([
UserRepository::class,
PostRepository::class,
CompanyRepository::class,
PostMergeableRepository::class,
PostAuthorizeRepository::class,
PostWithUnauthorizedFieldsRepository::class,
Expand Down
33 changes: 33 additions & 0 deletions tests/Migrations/2019_12_22_000005_create_Company_user__table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

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

class CreateCompanyUserTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('company_user', function (Blueprint $table) {
$table->foreignId('company_id')->constrained('companies');
$table->foreignId('user_id')->constrained('users');
$table->boolean('is_admin')->default(false);
$table->timestamps();
});
}

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