I usually create some helper methods on my controller that do all the logic. On our last project, I extracted all this logic out for a Service class that extends this base ApiService abstract class:
<?php
/**
* Copyright (c) 2016 IGET Serviços em comunicação digital LTDA - All rights Reserved
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
*/
namespace App\Services\Contracts;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
abstract class ApiService
{
/**
* @return array
*/
abstract public function getRelationships();
/**
* @return \Illuminate\Database\Eloquent\Model
*/
abstract public function getModel();
/**
* @param array $data
* @return \Illuminate\Database\Eloquent\Model
* @throws \Exception
*/
public function create(array $data)
{
\DB::beginTransaction();
try {
if (isset($data['relations'])) {
$relations = $data['relations'];
unset($data['relations']);
}
$created_model = $this->getModel()->create($data);
if (isset($relations)) {
$this->updateRelationships($created_model, $relations);
}
\DB::commit();
return $created_model;
} catch (\Exception $e) {
\DB::rollback();
throw $e;
}
}
/**
* @param array $data
* @param int $model_id
* @return \Illuminate\Database\Eloquent\Model
* @throws \Exception
*/
public function update(array $data, $model_id)
{
\DB::beginTransaction();
try {
$model = $this->getModel()->find($model_id);
if (isset($data['relations'])) {
$relations = $data['relations'];
unset($data['relations']);
}
$model->update($data);
if (isset($relations)) {
$this->updateRelationships($model, $relations);
}
\DB::commit();
return $model;
} catch (\Exception $e) {
\DB::rollback();
throw $e;
}
}
/**
* @param \Illuminate\Database\Eloquent\Model $model
* @param array $data
*/
private function updateRelationships(Model $model, array $data)
{
foreach ($this->getRelationships() as $relationship_name) {
if (isset($data[$relationship_name])) {
$relationship_type = get_class($model->$relationship_name());
switch ($relationship_type) {
case BelongsToMany::class:
$this->syncBelongsToManyRelationship($model, $relationship_name, $data[$relationship_name]);
break;
case MorphMany::class:
case HasMany::class:
$this->syncHasManyRelationship($model, $relationship_name, $data[$relationship_name]);
break;
default:
break;
}
unset($data[$relationship_name]);
}
}
}
/**
* @param \Illuminate\Database\Eloquent\Model $model
* @param $relationship_name
* @param array $data
*/
private function syncHasManyRelationship(Model $model, $relationship_name, array $data)
{
$present_ids = [];
foreach ($data as $related) {
$conditions = [
'id' => array_key_exists('id', $related) ? $related['id'] : null
];
$present_ids[] = $model->$relationship_name()->updateOrCreate($conditions, $related)->id;
}
$model->$relationship_name()->whereNotIn('id', $present_ids)->delete();
}
/**
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $relationship_name
* @param array $data
* @return mixed
*/
private function syncBelongsToManyRelationship(Model $model, $relationship_name, array $data)
{
return $model->$relationship_name()->sync($data);
}
}
Here you can see an example service implementation of a model that have a BelongsToMany contacts relationship:
<?php
/**
* Copyright (c) 2016 IGET Serviços em comunicação digital LTDA - All rights Reserved
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
*/
namespace App\Services;
use App\Models\Client;
use App\Services\Contracts\ApiService;
class ClientService extends ApiService
{
/**
* @return Client
*/
public function getModel()
{
return new Client();
}
/**
* @return array
*/
public function getRelationships()
{
return [
'contacts',
];
}
}
This code assumes that you will pass model relationship data on a relations key at your request payload.
Then on your controller, you should only do:
public function store(ClientRequest $request)
{
$created_client = $this->clientService->create($request->all());
return "your response";
}
I know that it feels complicated, but if you give a try, you will see that this speed up your development process and make your controllers very clean!
Feel free to use this class. It only implements BelongsMany, MorphMany and HasMany relationships.