action跳转控制器 php_PHP 程序结构设计:使用 Action 重构你的控制器

在 Spatie 的最新项目中,我们已经开始使用称为动作 (Action) 的概念。它使我们的控制器和模型保持苗条。这是一个简单的实践。我将在这篇文章中解释。

来自控制器和模型中的逻辑

考虑你又一个用 Laravel 开发的博客,您想在上面发布帖子。帖子发布后,应用会将它的标题和链接关联到推特。

执行该操作的控制器可能如下所示:

class PostsController

{

public function create()

{

// ...

}

public function store()

{

// ...

}

public function edit()

{

// ...

}

public function update()

{

// ...

}

public function delete()

{

// ...

}

public function publish(Post $post, TwitterApi $twitterApi)

{

$post->markAsPublished();

$twitterApi->tweet($post->title . PHP_EOL . $post->url);

flash()->success('Your post has been published!');

return back();

}

}

对我而言,非 CRUD 行为存在控制器中显得有点脏乱。让我们遵循 Adam’s advice 的建议,将 publish 方法放到它自己的控制器中。

class PublishPostController

{

public function __invoke(Post $post, TwitterApi $twitter)

{

$post->markAsPublished();

$twitter->tweet($post->title . PHP_EOL . $post->url);

flash()->success('Your post has been published!');

return back();

}

}

这已经比刚才好一点了,但是我们可以做的更好。加入你想要创建一个发布博文的命名。目前而言是不可能的,因为相关逻辑放置于控制器中。

为了相关逻辑可以从命令行(或应用的任何地方)调用,该逻辑不应该在控制器中。理想状态下,控制器唯一要做的就是处理 HTTP 层

你可能想将 publish 方法的的相关逻辑移动到 Post 模型中,对于小型项目而言很好。但是想象一下,与文章有关的动作很多,比如归档、复制等。所有的这些动作都将让你的模型变得巨大。

在动作中保存逻辑

与将逻辑写在控制器或者模型相比,让我们将其移到专用的类中。在 Spatie 公司,我们将其称之为动作。

动作是非常简单的类。只有一个公共的方法 execute,你也可以根据自己的喜好命名。

namespace App\Actions;

use App\Services\TwitterApi;

class PublishPostAction

{

/** @var \App\Services\TwitterApi */

private $twitter;

public function __construct(TwitterApi $twitter)

{

$this->twitter = $twitter;

}

public function execute(Post $post)

{

$post->markAsPublished();

$this->tweet($post->title . PHP_EOL . $post->url);

}

private function tweet(string $text)

{

$this->twitter->tweet($text);

}

}

markAsPublished 仍然可以通过 $post 实例调用,但是我们现在已经有了专门保存发布文章逻辑的地方,所以应当将代码逻辑移动到 PublishPostAction 中,让 Post 模型变得轻量化一些。

// in PublishPostAction

public function execute(Post $post)

{

$this->markAsPublished($post);

$this->tweet($post->title . PHP_EOL . $post->url);

}

private function markAsPublished(Post $post)

{

$post->published_at = now();

$post->save();

}

private function tweet(string $text)

{

$this->twitter->tweet($text);

}

在控制器中,你可以像这样调用 action:

namespace App\Http\Controllers;

use App\Actions\PublishPostAction;

class PublishPostController

{

public function __invoke(Post $post, PublishPostAction $publishPostAction)

{

$publishPostAction->execute($post);

flash()->success('Hurray, your post has been published!');

return back();

}

}

我们使用方法注入来解决 PublishPostAction,所以 Laravel 的 容器将自动将 TwitterApi 实例自身注入 PublishPostAction 中。

命令现在也可以使用 action 了。

namespace App\Console\Commands;

use Illuminate\Console\Command;

use App\Actions\PublishPostAction;

use App\Models\Post;

class PublishPostCommand extends Command

{

protected $signature = 'blog:publish-post {postId}';

protected $description = 'Publish a post';

public function handle(PublishPostAction $publishPostAction)

{

$post = Post::findOrFail($this->argument('postId'));

$publishPostAction->execute($post);

$this->comment('The post has been published!');

}

}

我们将其提取到 action 中还有另一个好处是,由于不再与 HTTP 层绑定,该代码更具有可测试性。

class PublishPostActionTest extends TestCase

{

public function setUp(): void

{

parent::setUp();

Carbon::setTestNow(Carbon::createFromFormat('Y-m-d H:i:s', '2019-01-01 01:23:45'));

TwitterApi::fake();

}

/** @test */

public function it_can_publish_a_post()

{

$post = factory(Post::class)->state('unpublished')->create();

(new PublishPostAction())->execute($post);

$this->assertEquals('2019-01-01 01:23:45', $post->published_at->format('Y-m-d H:i:s'));

TweetterApi::assertTweetSent();

}

}

可队列的 action

想象一下,你有个需要花费一些时间执行的 action。一个简单的方案就是为它创建一个队列并从 action 中调度工作。

让我们在 PublishPostAction 中使用队列去发送推文。

// in PublishPostAction

public function execute(Post $post)

{

$this->markAsPublished($post);

$this->tweet($post->title . PHP_EOL . $post->url);

}

private function markAsPublished(Post $post)

{

$post->published_at = now();

$post->save();

}

private function tweet(string $text)

{

dispatch(new SendTweetJob($text));

}

现在,如果你想在应用程序的其他地方发送推文,确保你可以像这样使用一个工作:

namespace App\Http\Controllers

class SendTweetController

{

public function __invoke(SendTweetRequest $request)

{

dispatch(new TweetJob($request->text);

flash()->success('The tweet has been sent');

return back();

}

}

这会完美地工作。但是,如果我们可以对任何事物使用 action,包括异步工作,那岂不是很好吗?

进入我们的 laravel-queueable-action 包。这个包使你可以轻松地操作队列。你可以通过提供的 QueueableAction 应用到 action,使其可排队。该特性增加了一个 onQueue 方法。

use Spatie\QueueableAction\QueueableAction;

namespace App\Actions;

class SendTweetAction

{

use QueueableAction;

/** @var \App\Services\TwitterApi */

private $twitter;

public function __construct(TwitterApi $twitter)

{

$this->twitter = $twitter;

}

public function execute(string $text)

{

$this->twitter->tweet($text);

}

}

现在我们调用 action,它将在队列中执行它的工作。

class SendTweetController

{

public function __invoke(SendTweetRequest $request, SendTweetAction $sendTweetAction)

{

$sendTweetAction->onQueue()->execute($request->text);

flash()->success('The tweet will be sent very shortly!');

return back();

}

}

你也可以通过传递其名称给 onQueue 来指定应该执行工作的队列。

$sendTweetAction->onQueue('tweets')->execute($request->text);

如果想要了解更多关于可队列的 action,请务必查看由我的同事和包创建者 Brent 发布的内容丰富的博客。

结束语

将逻辑提取到动作中使得在应用程序的多个地方都可以进行调用。也让代码测试变得更加简单。如果动作变大,可以将其分解成多个动作。

在 Spatie 公司,我们将这种操作命名为 execute 方法。你可以根据需要来进行调用。该实践并不是我们发明的,很多开发者早已经使用该方法。如果你来自于 DDD 的架构世界,你可能会注意到,动作只是将命令和处理程序包装在一起。

更多学习内容请访问:八重樱:怎么从一名码农成为架构师的必看知识点:目录大全(不定期更新)​zhuanlan.zhihu.com

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的官方群点击此处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用ThinkPHP框架中,默认的控制器后缀是没有后缀的。因此,在创建控制器时,不需要添加后缀。你可以通过使用`php think make:controller`命令来创建控制器,例如`php think make:controller app\index\controller\Blog`。这将创建一个名为Blog的控制器,没有后缀。 此外,值得注意的是,框架目录结构与ThinkPHP基本相同,但去除了控制器和行为的Controller和Action后缀。因此,在应用目录下,控制器类的命名空间必须以`app\`开头。 总结起来,使用ThinkPHP框架时,控制器没有后缀,而命名空间以`app\`开头。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [thinkphp6控制器](https://blog.csdn.net/tongkongyu/article/details/119937829)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [yt:yaf源码适应thinkphp](https://download.csdn.net/download/weixin_42108948/16050073)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值