>  기사  >  PHP 프레임워크  >  Laravel 단일 동작 컨트롤러 디자인의 매력

Laravel 단일 동작 컨트롤러 디자인의 매력

Guanhui
Guanhui앞으로
2020-05-22 11:24:403457검색

Laravel 단일 동작 컨트롤러 디자인의 매력

어제 Jeffrey Way는 사람들에게 컨트롤러 이름을 단수로 지정할 것인지 복수로 지정할 것인지 묻는 트윗을 게시했습니다. 나는 두 옵션 중 하나를 선택하지 않고 단일 작업 컨트롤러를 사용했다고 답했습니다. 다음에 일어나는 일은 어떤 사람들은 동의하고, 어떤 사람들은 동의하지 않으며, 어떤 사람들은 가장 이상한 일을 한다는 것입니다.

압도적인 반응으로 인해 제가 싱글 비헤이비어 컨트롤러를 좋아하는 이유와 이 컨트롤러가 훌륭하다고 생각하는 이유를 설명하는 글을 쓰고 싶었습니다.

우선, 글을 시작하기 전에 이 일에는 진실이 하나만 있는 것이 아니라는 점을 말씀드리고 싶습니다. 언제나 그렇듯이 모든 것은 개인 취향에 달려 있다는 점을 지적하고 싶습니다. 나는 단지 가르치고, 제안하고, 지적할 수 있을 뿐이며, 동의하고, 동의하지 않고, 받아들이고, 배우고/조정하는 것은 귀하에게 달려 있습니다. 아니면 둘 다. 이 블로그에서 원하는 것을 선택하고 편안함을 느낄 수 있는 모든 것을 하세요.

CRUD와 도메인 모델링 비교

시작하기 전에 먼저 우리가 유용하게 작성하는 CRUD 컨트롤러에 대해 생각해 보겠습니다. 나는 이것이 Laravel의 표준 관행이고 문서에 있는 대부분의 예제가 이 접근 방식을 사용하기 때문에 많은 사람들이 이 접근 방식을 고수할 것이라고 확신합니다. 게다가 각종 블로그나 앱 코드에서 자주 볼 수 있는 내용이기도 합니다.

하지만 가만히 생각해보면 이게 최선의 글쓰기 방법이 아닐까요? 이것이 소프트웨어 업계에서 일반적인 관행입니까? 최근 몇 년 동안 저는 도메인 중심 디자인(Domain Driven Design)과 같은 분야에서 소프트웨어가 귀하가 작업하는 도메인(Domian)에 어떻게 적용되고 어떻게 변환되는지에 대해 생각하는 데 많은 시간을 보냈습니다. 해당 분야의 유비쿼터스 언어를 모방하는 용어와 표현에 대해 생각하기 시작하면 코드가 더 명확해지고 요점이 더 명확해진다는 것을 알게 될 것입니다. (이 마지막 문장은 여전히 ​​고려하고 개선할 가치가 있습니다.)

마지막으로, 소프트웨어 작성의 본질은 도메인 프로세스를 최대한 적용하여 코드를 더 읽기 쉽고 유지 관리하기 쉽게 만드는 것이라고 믿습니다.

수완이 풍부한 컨트롤러는 이 두 가지 측면을 잘 수행하지 못합니다. 첫째, 도메인 측면보다는 데이터 측면에서 구조화하는 경향이 있기 때문에 읽기가 어렵습니다. 이 경우 상황에 따른 통제력을 잃게 됩니다. 데이터가 처리되는 방식을 보여주지만 정확히 어떤 일이 발생하는지 또는 데이터를 처리하는 데 어떤 프로세스를 사용하는지 설명하지 않습니다.

둘째, 유지 관리를 위해 최적화하지 않았습니다. 데이터 구조 측면에서 구축하고 있기 때문에 데이터 구조와도 결합됩니다. 실제로 도메인 모델은 지속적으로 발전하고 있으며 데이터 구조도 마찬가지입니다. 데이터 구조가 도메인의 여러 프로세스나 여러 부분을 처리하는 경우 조정하기 어려울 것입니다.

실용적인 예

이론은 지루하고 코드로 설명하는 것이 더 쉽기 때문에 실제적인 예를 살펴보겠습니다.

사용자가 이벤트를 조직할 수 있는 앱을 구축한다고 가정해 보겠습니다. 이러한 이벤트를 생성, 업데이트 및 삭제하는 방법을 제공하려고 합니다. 이는 CRUD 용어로 구현하는 방법에 대한 매우 일반적인 예입니다. 그럼, 이렇게 유능한 컨트롤러가 어떻게 변신하는지 살펴보겠습니다.

먼저 라우팅을 살펴보겠습니다:

Route::get('events', [EventController::class, 'index']);
Route::get('events/create', [EventController::class, 'create']);
Route::post('events', [EventController::class, 'store']);
Route::get('event/{event}', [EventController::class, 'show']);
Route::get('events/{event}/edit', [EventController::class, 'edit']);
Route::put('events/{event}', [EventController::class, 'update']);
Route::destroy('events/{event}', [EventController::class, 'destroy']);

이제 해당 컨트롤러:

<?php
namespace App\Http\Controllers;
use App\Models\Event;
final class EventController
{
    public function index()
    {
        // ...
    }
    public function create()
    {
        // ...
    }
    public function store()
    {
        // ...
    }
    public function show(Event $event)
    {
        // ...
    }
    public function edit(Event $event)
    {
        // ...
    }
    public function update(Event $event)
    {
        // ...
    }
    public function destroy(Event $event)
    {
        // ...
    }
}

이 EventController는 모든 CRUD 요청을 처리하고, 이벤트 목록을 표시하고, 지정된 이벤트를 표시하고, 이벤트를 생성하고, 기존 이벤트를 업데이트하고 삭제합니다. 이벤트.

인덱스 메소드의 세부 사항을 살펴보겠습니다.

public function index()
{
    $events = Event::paginate(10);
    return view(&#39;events.index&#39;, compact(&#39;events&#39;));
}

이 메소드에서는 이벤트를 검색한 다음 뷰에 제공하여 페이징 목록에 표시합니다. 지금까지는 너무 좋았습니다. 하지만 이제 다양한 페이지를 사용하여 과거 및 향후 이벤트를 보는 방법을 구현하려고 합니다. index 메소드에서 어떻게 구현하는지 살펴보겠습니다:

public function index(Request $request)
{
    if ($request->boolean(&#39;past&#39;)) {
        $events = Event::past()->paginate(10);
    } elseif ($request->boolean(&#39;upcoming&#39;)) {
        $events = Event::upcoming()->paginate(10);
    } else {
        $events = Event::paginate(10);
    }
    return view(&#39;events.index&#39;, compact(&#39;events&#39;));
}

으! 너무 지저분해 보이네요. 쿼리 논리를 숨기기 위해 Eloquent 범위를 사용했지만 여전히 추악한 연결 문이 있습니다. 이를 단일 동작 컨트롤러로 대체하는 방법을 살펴보겠습니다.

각 단일 동작 컨트롤러는 단 한 가지 작업만 수행합니다.

우선, 다양한 이벤트 목록을 얻기 위해 쿼리 매개변수를 사용하는 대신, 이를 달성하기 위해 전용 경로를 사용합니다.

Route::get(&#39;events&#39;, ShowAllEventsController::class);
Route::get(&#39;events/past&#39;, ShowPastEventsController::class);
Route::get(&#39;events/upcoming&#39;, ShowUpcomingEventsController::class);

이 루트는 이전 루트보다 조금 길지만 이전 루트보다 표현력이 더 풍부합니다. 어떤 컨트롤러가 어떤 특정 로직을 처리하는지 한눈에 확인할 수 있습니다. URL을 비교하면 가독성이 약간 향상되는 것을 볼 수 있습니다.

# Before
/events
/events?past=true
/events?upcoming=true
# After
/events
/events/past
/events/upcoming

이제 컨트롤러 중 하나를 살펴보세요. ShowUpcomingEventsController 컨트롤러를 살펴보세요.

<?php
namespace App\Http\Controllers;
use App\Models\Event;
final class ShowUpcomingEventsController
{
    public function __invoke()
    {
        $events = Event::upcoming()->paginate(10);
        return view(&#39;events.index&#39;, compact(&#39;events&#39;));
    }
}

추악한 if 문은 사라졌고 첫 번째 CRUD 컨트롤러 예시에서 사용했던 것과 동일한 읽기 가능한 세 개의 라이너가 생겼습니다. 하지만 이제 다른 모든 CRUD 작업을 수행하는 대신 전용 액션을 위한 전용 컨트롤러입니다.

간단하고 읽기 쉽고 유지 관리가 쉽습니다.

你可能会问自己,这样做值么,毕竟之前的 if 语句也没那么坏吧?但是我想向你展示的是你正在为未来的改进做优化,并改进维护性。下次你想要对这三个页面做任何指定改变的时候,你会知道在哪里改,并且不需要艰难地更新一个 if 语句。

当然,上面的例子很简单,我们来看一个更复杂一点的。我们试试重构 create 和 store 方法:

public function create()
{
    return view(&#39;events.create&#39;);
}
public function store(Request $request)
{
    $data = $request->validate([
        &#39;name&#39; => &#39;required&#39;,
        &#39;start&#39; => &#39;required&#39;,
        &#39;end&#39; => &#39;required|after:start&#39;,
    ])
    $event = Event::create($data);
    return redirect()->route(&#39;event.show&#39;, $event);
}

我们要做的就是把这两个方法移到专用的控制器,这样更好地解释了这些方法做了啥。这些方法更好地服务于你,比起把它们放在一个叫做 ScheduleNewEventController 的控制器中。我们接着更新这个控制器的路由:

Route::get(&#39;events/schedule&#39;, [ScheduleNewEventController::class, &#39;showForm&#39;]);
Route::post(&#39;events/schedule&#39;, [ScheduleNewEventController::class, &#39;schedule&#39;]);

我不会向你展示一个确切的控制器,因为它们有和上面的例子一样,有两个方法,只不过把 showForm 和 schedule 重新命名为更能表达它们干了啥的名字。即使这个不是单行为控制器,但是方法论是一样的:把你应用中的专用行为(方法)和它对应的控制器拆分到一起。

好了,现在你已经看了单行为控制器的例子了。你可能会想,这会导致越来越多的文件。但事实上,这个根本就不是问题。文件多又没啥。有更多、更小、更容易维护的文件比有更大、更难分析的要好。你可以打开一个单行为控制器的文件,然后快速扫描代码,马上就能知道这是干嘛的。

我经常把他们分组到不同的目录,这些目录负责领域的各个部分。这让你从文件结构的角度看控制器时,更加容易。

拆分控制器也让你跟容易找到特定的一个控制器。想象一下,你要寻找那个可以安排事件的控制器时。现在你只需要按照文件名搜索编辑器,而不是一个通用的 EventController。

其他情况

我也被问到是否要对所有控制器执行此操作。不总是。在命名控制器时,我倾向于严谨且简洁,但我也会像你一样适应各种情况。

当然,有时候你还是想用 resourceful 控制器。比如在你构建 RESTful API 时。这样做是很有意义,因为你经常直接与数据本身交互,而没有经常与领域或任何进程进行交互。CMS(内容管理系统)或 Laravel Nova 等应用程序就是最好的例子。

但是在需要的时候,您最好问问自己的方案是否更接近领域和处理过程。在需要根据领域执行操作的时候,比如 GraphQL 之类的或 API 之类的 RPC ,这样做可能更适合。

结论

我希望这有一点见地,你现在能更理解我为什么如此喜欢单行为控制器了吧。我相信,结合小的 classes,再使用无处不在的语言、显式地命名,会带来更可维护的代码,甚至是控制器,不仅仅是领域对象。但是正如我开头所说,选择能帮助你的部分,好好分辨哪些适用于你,哪些不行。

推荐教程:《PHP教程》《Laravel教程

위 내용은 Laravel 단일 동작 컨트롤러 디자인의 매력의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 learnku.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제