В моем проекте я дублировал функции в своих контроллерах, так как каждый контроллер должен иметь схожую функциональность, но повторять код кажется немного грязным.
И в моем EventController, и в моем ArticleController у меня есть дублированная функция под названием handleTags(), которая буквально делает одно и то же в каждой модели.
Код выглядит так:
/**
* Handle tagging of this resource
*
* First, get the tags field and make sure it isn't empty
* Convert the string into an array and loop through the array
* Create new tags if they don't exist
*
* @param Request $request: data from the request
* @param int $id: the ID of the model instance have tags synced to
*/
public function handleTags(Request $request, $id)
{
$event = Event::find($id);
if ($request->has('tags')) {
$tags = $request->get('tags');
if (!empty($tags)) {
// Turn a String into an array E.g. one, two
$tagArray = array_filter(explode(", ", $tags));
// Loop through the tag array that we just created
foreach ($tagArray as $tag) {
Tag::firstOrCreate([
'name' => ucfirst(trim($tag)),
'slug' => str_slug($tag)
]);
}
// Grab the IDs for the tags in the array
$tags = Tag::whereIn('name', $tagArray)->get()->pluck('id');
$event->tags()->sync($tags);
} else {
// If there were no tags, remove them from this model instance
$event->tags()->sync(array());
}
}
}
Можно ли было бы переместить эту функциональность в черту? Что-то вроде Taggable?
Затем вы могли бы вызвать handleTags() в соответствующих контроллерах через свойство, точно так же, как Searchable предоставляет вам доступ к методу search()?
Сам Laravel уже украшает свой шаблонный контроллер различными чертами, такими как use AuthorizesRequests, DispatchesJobs, ValidatesRequests;, поэтому нет ничего плохого в использовании черт.
Вам даже не нужно будет создавать другую функцию в контроллере, чтобы обернуть эту функцию. Вы можете просто использовать этот метод в своих маршрутах напрямую (конечно, только при желании).






Я думаю, что лучшим решением будет сделать черту Модель, я объясню сам.
trait HasTags {
public function handleTags($tags)
{
$tags = array_filter(explode(", ", $tags))
$tags = array_map(function () {
return Tag::firstOrCreate([
'name' => ucfirst(trim($tag)),
'slug' => str_slug($tag)
]);
}, $tags)
$this->tags()->sync(collect($tags)->pluck('id'))
}
public function tags()
{
return $this->morphMany(Tag::class);
}
}
Модель
class Event extends Model
{
use HasTags;
}
Контроллер
$event = Event::find($id);
if ($request->has('tags')) {
$event->handleTags($request->get('tags'));
}
Я пишу это очень быстро и без тестирования, но это общая идея. вы можете провести дополнительный рефакторинг, используя все манипуляции с массивами с коллекциями.
Сделать трейт можно в app/Http/Traits/TaggableTrait.php
Вам просто нужно передать объект вместо идентификатора, чтобы функция не зависела от типа класса.
тогда ваша черта будет примерно такой:
namespace App\Http\Traits;
use App\Tag;
trait TaggableTrait
{
/**
* @param Request $request: data from the request
* @param App\Article | App\Event $object: the model instance have tags synced to
*/
public function handleTags(Request $request, $object)
{
if ($request->has('tags')) {
$tags = $request->get('tags');
if (!empty($tags)) {
// Turn a String into an array E.g. one, two
$tagArray = array_filter(explode(", ", $tags));
// Loop through the tag array that we just created
foreach ($tagArray as $tag) {
Tag::firstOrCreate([
'name' => ucfirst(trim($tag)),
'slug' => str_slug($tag)
]);
}
// Grab the IDs for the tags in the array
$tags = Tag::whereIn('name', $tagArray)->get()->pluck('id');
$object->tags()->sync($tags);
} else {
// If there were no tags, remove them from this model instance
$object->tags()->sync(array());
}
}
}
}
EventController
use App\Http\Traits\TaggableTrait;
class EventController extends Controller
{
use TaggableTrait;
/*** ***** */
public function update(Request $request, $id)
{
/** ***/
$event = Event::findOrFail($id);
handleTags($request, $event);
/*** *** */
}
}
Можно ли иметь черты на контроллере?
да, так мы используем Laravel, посмотрите LoginController
конечно, php вас не остановит