你所不知道 ❌ Resource

本文探讨如何通过合理组织控制器和路由,实现更简洁、更符合RESTful风格的Web应用设计。通过将相关资源进行整合,不仅可以减轻控制器负担,还能使代码结构更加清晰。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

找我请到 掘金 或者 Github

自己也维护不过来那么多站点,对不住大家了。

? 更新平台多偶尔会漏掉,如果觉得文章还行点个 star 防走失。
你所不知道 ❌ 系列一起探索未知

很久没写文章了,在新的公司新的遇到了新的伙伴,胖丁哥哥让我看了 laracon 2017 - Adam Wathan 的视频,略微手痒想分享一下自己的东西,这边使用的是 laravel 作为讲解,但是思想却不局限于于 laravel 或者 php
呦呦呦呦,这边是差不多的小二先生~~~~

差不多的 路由

平时我们写代码的时候经常会写出很多下面这样的路由:

Route::get('/podcasts', 'PodcastsController@index');
Route::get('/podcasts/create', 'PodcastsController@create');
Route::post('/podcasts', 'PodcastsController@store');
Route::get('/podcasts/{id}', 'PodcastsController@show');
Route::get('/podcasts/{id}/edit', 'PodcastsController@edit');
Route::patch('/podcasts/{id}', 'PodcastsController@update');
Route::delete('/podcasts/{id}', 'PodcastsController@destroy');

Route::post('/podcasts/{id}/update-cover-image', 'PodcastsController@updateCoverImage');
Route::post('/podcasts/{id}/subscribe', 'PodcastsController@subscribe');
Route::post('/podcasts/{id}/unsubscribe', 'PodcastsController@unsubscribe');
Route::post('/podcasts/{id}/publish', 'PodcastsController@publish');
Route::post('/podcasts/{id}/unpublish', 'PodcastsController@unpublish');

Route::get('/episodes', 'EpisodesController@index');
Route::get('/episodes/{id}', 'EpisodesController@show');
Route::get('/episodes/{id}/edit', 'EpisodesController@edit');
Route::patch('/episodes/{id}', 'EpisodesController@update');

Route::get('/podcasts/{id}/episodes', 'PodcastController@indexEpisode');
Route::post('/podcasts/{id}/episodes', 'PodcastController@storeEpisode');
Route::get('/podcasts/{id}/episodes/new', 'PodcastController@createEpisode');

应该非常熟悉这样所谓 嵌套资源,随着项目的扩大,这样会使得控制器一个个的变得胖起来逻辑开始复杂起来,现在让我们开始为这差不多的路由做个变身。

差不多的 CURD / REST

这边进行一个小插曲,对资源总结起来大概就是 7 个标准的 Action :

  • Index - 用来展示所有的资源项,比如所有用户。
  • Show - 用来展示单个的资源项,比如用户详情。
  • Create - 用来显示创建资源的页面,前后端分离可能就没这个 Action 。
  • Store - 用来接受数据并创建资源项,比如创建用户。
  • Edit - 用来显示编辑资源的页面,前后端分离可能就没这个 Action 。
  • Update - 用来接受数据并修改资源项,比如保存用户详情。
  • Destroy - 用来删除指定的资源项,比如删除用户。

在后面的路由列表中,我们把只带有这 7 种 Action 的路由器都写成 Resource

差不多的 小问题

在上面的路由中我们选择一条常见的路由来做变身:

GET /podcasts/{id}/episodes

对于这样的一个 URL,如果我们只想让控制器只拥有 7 个标准的 Action ,我们应该把它放在哪个控制器呢?

差不多的 控制器

放在 PodcastsController 控制器中吗?那这样就会与控制器中的 Index Action 冲突了。
放在 EpisodesController 控制器中吗?这样也会与控制器中的 Index Action 冲突。

GET /podcasts/{id}/episodes => Index
GET /podcasts/              => Index
GET /episodes/              => Index

那我们需要怎么安放这个到处被人嫌弃的 URL 呢?

不一样的 控制器

其实我们可以把 podcasts 和 episodes 合起来当做一种资源,存放在 PodcastEpisodesController 中。

class PodcastEpisodesController extends Controller
{
    public function index($id)
    {
        $podcast = Podcast::with('episodes')->findOrFail($id);
        return response()->json(['podcast' => $podcast]);
    }
}

不一样的 路由

Route::resource('podcasts', 'PodcastsController');
Route::resource('episodes', 'EpisodesController');
Route::resource('podcasts.episodes', 'PodcastEpisodesController');

Route::post('/podcasts/{id}/update-cover-image', 'PodcastsController@updateCoverImage');
Route::post('/podcasts/{id}/subscribe', 'PodcastsController@subscribe');
Route::post('/podcasts/{id}/unsubscribe', 'PodcastsController@unsubscribe');
Route::post('/podcasts/{id}/publish', 'PodcastsController@publish');
Route::post('/podcasts/{id}/unpublish', 'PodcastsController@unpublish');

按照这个思路来进行路由的变身,我们将会得到三个控制器:

  • PodcastsController 拥有 7个标准 Action,5个非标准的 Action
  • EpisodesController 拥有 4个标准 Action
  • PodcastEpisodesController 拥有 3个标准 Action

差不多的 问题

虽然经历瘦身后,路由列表已经变得很短了,但是PodcastsController 中还有 5 个非标准的 Action,我们将继续对这些方法进行瘦身:

POST /podcasts/{id}/update-cover-image

不一样的 控制器

这个 URL 是用来更新 podcasts 的封面图片的,我们是不能把封面图片也单独看成一种资源呢?显然是可以的,这个资源中包含了一个更新的方法。

class PodcastCoverImageController extends Controller
{
    public function update($id)
    {
        $podcast = Auth::user()->podcasts()->findOrFail($id);
        $podcast->update([
            'cover_path' => request()->file('cover_image')->store('images', 'public')
        ]);
        return response()->json(['message' => 'ok']);
    }
}

不一样的 路由

这个时候新的路由可以写成:

Route::put('/podcasts/{id}/cover-image', 'PodcastCoverImageController@update');

新的路由表可以写为:

Route::resource('podcasts', 'PodcastsController');
Route::resource('episodes', 'EpisodesController');
Route::resource('podcasts.episodes', 'PodcastEpisodesController');
Route::resource('podcasts.cover-image', 'PodcastCoverImageController');

Route::post('/podcasts/{id}/subscribe', 'PodcastsController@subscribe');
Route::post('/podcasts/{id}/unsubscribe', 'PodcastsController@unsubscribe');
Route::post('/podcasts/{id}/publish', 'PodcastsController@publish');
Route::post('/podcasts/{id}/unpublish', 'PodcastsController@unpublish');

差不多的 中间表问题

刚才我们讨论的两个问题都是对于普通的表进行操作,但是如果我们修改和创建的数据在中间表上我们又该如何呢?

Route::post('/podcasts/{id}/subscribe', 'PodcastsController@subscribe');
Route::post('/podcasts/{id}/unsubscribe', 'PodcastsController@unsubscribe');

这两个路由,分别对 user_podcast 中间表的进行删除数据和创建数据。

不一样的 控制器

其实我们可以把中间表也看做一种资源,写成 SubscriptionsController,其中里面包含两个 Action 有 storedestroy。按照这个思路可把剩下的两个控制器写入 PublishedPodcastsController,也是包含了 storedestroy Action。

不一样的 路由

经过这么瘦身下来,我们的路由表变成这个样子:

Route::resource('podcasts', 'PodcastsController');
Route::resource('episodes', 'EpisodesController');
Route::resource('podcasts.episodes', 'SubscriptionsController');
Route::resource('podcasts.cover-image', 'PodcastCoverImageController');
Route::resource('subscriptions', 'SubscriptionsController');
Route::resource('published-podcasts', 'PublishedPodcastsController');

惊喜不惊喜,刺激不刺激,好看不好看,简洁不简洁!!!

结尾

其实,我们可以把 Everything 都看做是资源,对其进行 CURD 的操作,带来的好处也是显而易见,更加轻的控制器,更加进行的分类,更加的 RESTful。

相关资源

一起成长

如果您感觉有收获可以点赞关注激励我,也欢迎到 Github 加个 star。

微信公众号

本文原稿来自 PushMeTop
<think>我们正在讨论5G NR中DCI编码的问题。用户特别关注:在DCI格式经过大小限制(size restriction)和对齐(alignment)操作之后,哪些格式的DCI信息的第一个比特是固定的。用户提到,这里的DCI信息是指已经经过DCI格式大小限制和对齐后的比特序列。 根据之前的对话,我们得出的结论是:在公共搜索空间(CSS)中,DCI格式0_0和1_0的首个比特是固定的,它作为格式指示位(flag for format 0_0/1_0 differentiation)。但是用户指出当前关于该问题的结论存在一致,因此我们需要重新审视并确认。 参考引用: [^1]:如果DCI format 0_0长度大于DCI format 1_0,那么DCI format 0_0会通过截断频率域资源分配(frequency domain resource assignment)字段的高位比特来使两者长度相同。 [^2]:这段引用讨论的是视频编码中的量化过程,与DCI编码无关,因此可以忽略。 [^3]:这段引用描述的是PDCCH的处理流程,包括DCI的CRC添加、RNTI加扰、编码、复用等,但没有直接回答首比特固定的问题。 因此,我们主要依据引用[^1]以及5G NR标准的相关知识。 回顾: 在公共搜索空间(CSS)中,为了减少UE的盲检测次数,标准要求DCI format 0_0和DCI format 1_0必须具有相同的负载大小(payload size)。如果它们的原始长度同,则需要进行长度对齐操作。对齐的方法通常是对较长的DCI进行截断(通常是截断频率域资源分配字段的高位比特)或者对较短的DCI进行补零(padding)。 对齐后,DCI format 0_0和DCI format 1_0具有相同的长度。在它们的比特序列中,第一个比特是一个标志位,用于区分两种格式: - 如果第一个比特为0,则表示该DCI是format 0_0(用于上行调度)。 - 如果第一个比特为1,则表示该DCI是format 1_0(用于下行调度)。 这个标志位是固定的,即它总是位于对齐后比特流的第一个比特位置。 对于其他DCI格式(如0_1, 1_1, 2_x等),它们通常出现在专用搜索空间(USS)中,没有强制长度对齐的要求,因此它们的第一个比特是固定的,而是取决于具体的DCI内容。 因此,结论是:只有公共搜索空间中的DCI format 0_0和DCI format 1_0(经过对齐后)的首个比特是固定的,作为格式指示位。 现在,我们需要回答用户的问题,并确保结论一致。 回答结构: 1. 明确回答:在公共搜索空间(CSS)中,DCI format 0_0和DCI format 1_0经过对齐后的首个比特是固定的。 2. 解释对齐过程:当两者长度一致时,通过截断DCI format 0_0的FDRA字段的高位比特(如果它比DCI format 1_0长)或者对较短的进行补零,使得两者长度相等。 3. 强调首比特的作用:它是一个标志位,用于区分格式(0_0或1_0)。 4. 其他格式:在专用搜索空间(USS)中的DCI格式(如0_1, 1_1等)没有这样的对齐要求,因此首比特固定。 注意:用户提到“当前关于该问题的结论存在一致”,因此我们需要确保回答的准确性。根据3GPP TS 38.212标准,在公共搜索空间中,DCI format 0_0和1_0必须具有相同的负载大小,并且使用第一个比特作为格式区分标志。 因此,最终答案如下:</think>### DCI编码中首个比特固定性的分析 在5G NR系统中,经过DCI格式大小限制和对齐操作后,**只有公共搜索空间(CSS)中的DCI format 0_0和DCI format 1_0的首个比特是固定的**。这一特性通过标准定义的长度对齐机制实现,具体分析如下: #### 1. **固定首比特的格式与位置** | DCI格式 | 搜索空间 | 首比特固定性 | 比特位置 | 比特含义 | |---------------|-----------|-------------|----------|----------------------| | **0_0** | 公共CSS | ✅ 固定 | 第1位 | 格式指示位(0=0_0) | | **1_0** | 公共CSS | ✅ 固定 | 第1位 | 格式指示位(1=1_0) | | 其他格式 | 专用USS | 固定 | - | - | #### 2. **对齐机制保障首比特固定** - **长度对齐原则**: - 当DCI 0_0原始长度 > DCI 1_0时:截断0_0的频域资源分配(FDRA)字段高位比特[^1] - 当DCI 1_0原始长度 > DCI 0_0时:在1_0中填充零比特 - 数学表达:$L_{\text{aligned}} = \max(L_{0_0}, L_{1_0})$ - **格式指示位保护**: ```mermaid graph TD A[原始DCI 0_0] --> B{长度比较} C[原始DCI 1_0] --> B B -->|L0>L1| D[截断0_0的FDRA高位] B -->|L1>L0| E[1_0低位补零] D & E --> F[对齐后首比特=格式指示位] ``` 无论对齐操作如何修改FDRA字段,格式指示位始终位于比特流最前端且被修改[^1]。 #### 3. **固定性原理** - **减少盲检复杂度**:UE通过首比特即可区分调度类型,避免同时解码两种格式 - **标准强制要求**:3GPP TS 38.212明确规定CSS中0_0/1_0必须等长[^1] - **比特流结构保障**: ```python # 对齐前:0_0 = [标志位] + [其他字段] # 对齐操作只修改[其他字段],标志位位置变 ``` #### 4. **非固定首比特的格式** 在专用搜索空间(USS)中,所有DCI格式(如0_1/1_1/2_x)均**无首比特固定特性**,原因包括: - 无强制长度对齐要求 - 字段结构动态变化 - UE通过RNTI加扰和特定搜索空间定位 ### 结论 **仅公共搜索空间中的DCI format 0_0和1_0在长度对齐后具有固定首比特**,该比特作为格式指示位(0/1)始终位于比特流起始位置。这一特性通过标准强制长度对齐机制实现,核心目的是降低UE盲检复杂度。其他DCI格式因无对齐要求,首比特固定[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值