【问题标题】:PHP framework URL conventionsPHP 框架 URL 约定
【发布时间】:2009-06-22 21:45:43
【问题描述】:

很多框架都使用像 /controller/action/{id} 这样的 URL 约定,这很棒,但是如果您需要除此之外的任何配置,则由您自己编写路由。

您将如何在后端处理像 /users/{id}/friends 这样的 URL? (列出用户的所有朋友)

我认为在控制器中,这样的事情是合适的:

class User {
    function index() {
        echo 'user index';
    }
}

class Friend extends User {
    function index($user_id) {
        echo 'friend index';
    }    
}

那么你会得到以下地图:

/users              -> User::index()
/users/{id}         -> User::view($id)
/users/{id}/friends -> Friend::index($user_id)

我想把 Friend 类放在 User 类中,但显然你不能在 PHP 中这样做,所以这是我能想到的最好的方法。想法?

将使用哪个 URL 来编辑您的朋友列表? /users/{id}/friends/edit 可以工作,但似乎不合适,因为您永远不应该编辑别人的朋友列表。 /account/friends/edit 会是更好的选择吗?你会把相应的代码放在哪里?在朋友控制器、用户控制器或专用帐户控制器中?

额外问题:您更喜欢哪个? /photos/delete/{id}/photos/{id}/delete


答案:

所以,我从答案中收集到的是,如果“事物”很复杂(如“朋友”)但没有自己的控制器,你可以给它一个不带模型的控制器,或者如果它没有,你应该把它和它最密切相关的东西塞进去。您的 URL 不应影响您放置代码的位置。大多数人似乎认为您应该尽可能坚持使用/controller/action/{id},因为这是人们所熟悉的。

除了说它“尴尬”之外,没有人真正评论扩展课程。如果我真的想把 FriendList 分开的话,也许 FriendList 会是一个更合适的类。

感谢所有答案:)

【问题讨论】:

  • 您正在对“很多框架”进行概括——您实际上在谈论哪些?
  • @Sean:这有关系吗?我想到了 CakePHP,但 Kohana 和我相信 RoR 使用了类似的东西。这不是关于那些框架,而是关于什么约定有意义

标签: php url-routing conventions


【解决方案1】:

你所说的路线,以及你使用子类来实现这种结构的方式,对我来说似乎有点尴尬。 /controller/action/{id} 的标准约定非常适合简单的操作,但如果您正在创建一个复杂的应用程序,您将始终需要创建自定义路由。创建这些路由时可能有一些很好的指导方针可供使用,但归根结底是在您的应用程序中保持一致并尽可能简单。

我认为没有任何充分的理由将 /user/{id}/friends 映射到“Friend”控制器。为什么不让“friends”成为User 控制器上的一个动作?一旦您真正深入查看特定朋友的页面,您可以使用 Friend 控制器 (/friends/view/123) 或者您可以重新调整 User 控制器的用途,使其适用于朋友或当前登录的用户 (@987654329 @)。

Re:额外的问题,我会坚持使用/photos/delete/{id} (/controller/action/{id}),因为这是最广泛接受的机制。

【讨论】:

    【解决方案2】:

    我更喜欢/photos/{id}/delete。我的理由是,如果您从 URL 的末尾删除一个组件,它仍然应该是有意义的。

    很容易假设/photos/{id} 应该做什么:查看该{id} 的照片集。

    但是/photos/delete 应该怎么做呢?这真的不清楚。

    我知道有/controller/action/id 的默认约定,但该组织是为了映射到控制器的类/方法架构。我认为组织 UI 以适应代码并不是一个好主意(URL 在某种程度上是 UI 的一部分)。


    Re cmets:是的,/photos/{id} 可能更适合通过其 id 查看给定照片。 /users/{id}/photos 或许可以查看收藏。由你决定。

    关键是您应该从用户的角度考虑 UI,而不是从代码组织的角度考虑。

    【讨论】:

    • 听起来很合理。为此编写代码也不是太难。但是剩下的东西呢?
    • 这很好。正是出于这个原因,/controller/action/id 约定可能最适合 Web 服务而不是面向公众的网站。我想这取决于您要构建的更多的是面向用户的内容网站还是面向开​​发人员/管理员的应用程序或服务。
    • PS:我会假设photos/{id} 查看的是 {id} 给出的照片,而不是集合 :) 如果你想要一个集合,我会使用 /users/{id}/photos 来显示所有照片用户。
    • @pix0r:仅供参考,它非常面向用户
    【解决方案3】:

    您可以选择或。问题是当你将两者混合时。 /users/{id}/friends 和 /users/friends/{id} 当某人的 id 为“friends”时,这将失败。这似乎是一个微不足道的案例,但使用用户名作为 id 非常流行。您必须限制每个操作的用户名。


    有时你做不到/{controller}/{action}/{id}

    前段时间我做了一个独立音乐网站,我们做了

    /artist/{username}
    /artist/{username}/albums
    /artist/{username}/albums/{album}
    

    我们不想测试条件,所以我们没有这样做

    /artist/{username}/{album}
    

    因为我们不想检查任何拥有名为“albums”的专辑的人

    我们本来可以做到的

    /artist/{username}
    /artist/{username}/albums
    /albums/{album}
    

    但是这样我们就会失去在 URL 中同时包含艺术家姓名和专辑名称的 SEO 优势。同样在这种情况下,我们会强制专辑名称是唯一的,这会很糟糕,因为艺术家的专辑名称与其他艺术家相同是很常见的。

    你可以做纯粹的/{controller}/{action}/{id},但你会失去一些搜索引擎优化,你不能做网址缩短。

    /artist/view/{username}
    /artist/albums/{username}
    /album/view/{album}
    

    回到你的例子。

    /users/{id}/friends/edit 可以工作, 但这似乎不合适,因为 你永远不应该编辑某人 else 的好友列表。

    在这种情况下,它应该是/friends/edit,因为假设您以某种方式在会话中,您的用户 ID 是重复的信息。一般来说,您希望支持 URL 缩短而不是 URL 扩展。

    (奖金问题) 也不是,我会使用RESTDELETE /photo?id={id}

    【讨论】:

    • 您所说的一切听起来都不错,但这仍然留下了将代码放在哪里的问题。您将 /artists/{username}/albums 的处理程序放在哪里?在艺术家控制器、专辑控制器或其他地方?我在这个问题stackoverflow.com/questions/1016258 中考虑了 REST,但是当它出现时,你不能使用 HTML 发出 DELETE,所以你需要一个不同的 URL,如果你真的想要,它可以反过来在后端发出 DELETE。跨度>
    【解决方案4】:

    这还取决于您存储数据的方式。我可以想象在某些情况下,您需要一个“朋友列表”作为模型中的实体。一个合乎逻辑的方法是为每个好友列表指定一个唯一标识符,一个主键。

    这在逻辑上会导致以下路线,因为您只需要朋友列表的主键即可编辑或删除它...

    /friends/edit/{friendListId}

    由您决定。正如 pix0r 所说:小型应用程序的约定是 /{controller}/{action}/{id} 其中 {id} 应该是可选的,以匹配您的大多数网站操作。在某些情况下,应用程序变得很大,并且您希望定义具有 3 个以上元素的特定路由。在某些情况下,某些实体只会获得更大的意义(上例),您可以决定为它定义一个自定义控制器(这使得默认路由再次完美......)。

    我会坚持使用默认路由/controller/action/id,但只是不要一开始就为所有东西(比如朋友)制作控制器。只要您的所有路由链接和操作(表单等)都是基于路由和操作生成的,模型-视图-控制器模式就可以让您非常轻松地稍后更改路由。所以你真的不用那么麻烦:)

    【讨论】:

    • 当然,但我没有好友列表。朋友与特定用户相关联,而不是与组相关联。再说一次,我如何设置我的特定系统并不是真正的重点,问题是您将如何处理那些您没有为此类事情设置单独实体的情况?在你的路由不能归结为 /controller/action/id 的情况下,你会怎么做?你把代码放在哪里?
    • 在您的情况下,您仍然可以归结为 /controller/action/id => /user/editfriends/{userId} ?这里的问题是你是否想要它(阅读:你的系统设计)。好友列表有多复杂?它需要自己的控制器吗?这应该会引导您将代码放在哪里..?
    • Hrm.... 所以如果它很复杂,给它自己的控制器,否则将它与它最密切相关的东西塞进去?
    • 很快:是的。您应该只在逻辑上定义相互关联的操作组。想想你的功能,模块化整个事情,你会看到控制器:)
    【解决方案5】:

    网址本身并不重要。更重要的是每个控制器中的内容。在您的示例中,您的朋友列表扩展了 User 类。如果你的朋友列表真的只是一个用户列表,也许它应该扩展 Users 控制器,以便你在一个地方处理用户列表。

    class Users {
    
        public function index() {
            $users = $this->findUsers();
        }
    
        protected function findUsers($userId=null) { ... }
    }
    
    class Friends extends Users {
    
        public function index($userId) {
            $users = $this->findUsers($userId);
        }
    }
    

    如果您很难确定要扩展哪个类,请从每个类中写出您需要的内容,然后选择列表最长的一个。

    【讨论】:

    • 我的逻辑有点不同。如果是 /users/{id}/photos (照片不是用户)来获取特定用户拥有的所有照片,我会以同样的方式扩展它。这个想法是为了避免函数名称冲突(而不是将朋友函​​数直接放入用户类中),同时仍然能够查找拥有该项目的用户。但在这种情况下,我需要同时访问用户模型和照片模型。在这种情况下,“Photo”是一个已经存在的类……将函数放在 User 类或 Photo 类中是否更合适,我将命名什么
    • -- 函数? User::listFriends() 或 Photo::byUser()??我有没有明确答案的设计决策:(
    • 使用控制器将用户与他们的照片联系起来,因为这是业务逻辑。你的 User 和 Photo 模型应该只封装它们自己的数据的状态和操作。这意味着您实现 Photo::byUser($userId) 因为您正在检索照片列表。您在一些同时具有用户模型和照片模型的控制器 UserPhotos 中进行此调用
    • 这样会得到很多 很多 个连接器对象,不是吗?好的,谢谢您的意见。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多