@ParamConverter
用法
@ParamConverter
注解调用*转换器*将请求参数转换为对象。这些对象存储为请求属性,因此可以作为控制器方法参数注入
1 2 3 4 5 6 7 8 9 10
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* @Route("/blog/{id}")
* @ParamConverter("post", class="SensioBlogBundle:Post")
*/
public function show(Post $post)
{
}
幕后发生了一些事情
- 转换器尝试从请求属性中获取
SensioBlogBundle:Post
对象(请求属性来自路由占位符 -- 这里是id
); - 如果未找到
Post
对象,则生成404
响应; - 如果找到
Post
对象,则定义一个新的post
请求属性(可通过$request->attributes->get('post')
访问); - 对于其他请求属性,当方法签名中存在时,它会自动注入到控制器中。
如果您像上面的示例一样使用类型提示,您甚至可以省略 @ParamConverter
注解
1 2 3 4
// automatic with method signature
public function show(Post $post)
{
}
您可以通过将 auto_convert
标志设置为 false
来禁用类型提示方法参数的自动转换功能
1 2 3 4 5
# config/packages/sensio_framework_extra.yaml
sensio_framework_extra:
request:
converters: true
auto_convert: false
您还可以按名称显式禁用某些转换器
1 2 3 4 5
# config/packages/sensio_framework_extra.yaml
sensio_framework_extra:
request:
converters: true
disable: ['doctrine.orm', 'datetime']
要检测在参数上运行哪些转换器,将运行以下过程
- 如果使用
@ParamConverter(converter="name")
显式选择了转换器,则选择具有给定名称的转换器。 - 否则,所有已注册的参数转换器都按优先级迭代。调用
supports()
方法来检查参数转换器是否可以将请求转换为所需的参数。如果它返回true
,则调用参数转换器。
内置转换器
此扩展包有两个内置转换器,Doctrine 转换器和 DateTime 转换器。
Doctrine 转换器
转换器名称: doctrine.orm
Doctrine 转换器尝试将请求属性转换为从数据库中获取的 Doctrine 实体。有几种不同的方法是可能的
1) 自动获取
如果您的路由通配符与实体上的属性匹配,则转换器将自动获取它们
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/**
* Fetch via primary key because {id} is in the route.
*
* @Route("/blog/{id}")
*/
public function showByPk(Post $post)
{
}
/**
* Perform a findOneBy() where the slug property matches {slug}.
*
* @Route("/blog/{slug}")
*/
public function show(Post $post)
{
}
自动获取在以下情况下有效
- 如果
{id}
在您的路由中,则使用它通过find()
方法按主键获取。 - 转换器将尝试通过使用路由中*所有*实际上是实体属性的通配符(非属性将被忽略)来执行
findOneBy()
获取。
您可以通过实际*添加* @ParamConverter
注解并使用 @ParamConverter 选项来控制此行为。
2) 通过表达式获取
如果自动获取不起作用,请使用表达式
1 2 3 4 5 6 7 8 9
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity;
/**
* @Route("/blog/{post_id}")
* @Entity("post", expr="repository.find(post_id)")
*/
public function show(Post $post)
{
}
使用特殊的 @Entity
注解和 expr
选项,通过调用存储库上的方法来获取对象。repository
方法将是您实体的 Repository 类,任何路由通配符(如 {post_id}
)都可用作变量。
提示
@Entity
注解是使用 expr
的快捷方式,并且具有与 @ParamConverter
相同的所有选项。
这也可以用于帮助解决多个参数
1 2 3 4 5 6 7
/**
* @Route("/blog/{id}/comments/{comment_id}")
* @Entity("comment", expr="repository.find(comment_id)")
*/
public function show(Post $post, Comment $comment)
{
}
在上面的示例中,$post
参数是自动处理的,但 $comment
是使用注解配置的,因为它们不能都遵循默认约定。
DoctrineConverter 选项
@ParamConverter
或 (@Entity
) 注解上提供了许多 options
来控制行为
id
: 如果配置了id
选项并与路由参数匹配,则转换器将按主键查找1 2 3 4 5 6 7
/** * @Route("/blog/{post_id}") * @ParamConverter("post", options={"id" = "post_id"}) */ public function showPost(Post $post) { }
mapping
: 配置与findOneBy()
方法一起使用的属性和值:键是路由占位符名称,值是 Doctrine 属性名称1 2 3 4 5 6 7 8
/** * @Route("/blog/{date}/{slug}/comments/{comment_slug}") * @ParamConverter("post", options={"mapping": {"date": "date", "slug": "slug"}}) * @ParamConverter("comment", options={"mapping": {"comment_slug": "slug"}}) */ public function showComment(Post $post, Comment $comment) { }
exclude
配置应在findOneBy()
方法中使用的属性,方法是*排除*一个或多个属性,以便不使用*所有*属性1 2 3 4 5 6 7
/** * @Route("/blog/{date}/{slug}") * @ParamConverter("post", options={"exclude": {"date"}}) */ public function show(Post $post, \DateTime $date) { }
strip_null
如果为 true,则当使用findOneBy()
时,任何值为null
的值将不会用于查询。entity_manager
默认情况下,Doctrine 转换器使用*默认*实体管理器,但您可以配置它1 2 3 4 5 6 7
/** * @Route("/blog/{id}") * @ParamConverter("post", options={"entity_manager" = "foo"}) */ public function show(Post $post) { }
evict_cache
如果为 true,则强制 Doctrine 始终从数据库而不是缓存中获取实体。
DateTime 转换器
转换器名称: datetime
datetime 转换器将任何路由或请求属性转换为 datetime 实例
1 2 3 4 5 6
/**
* @Route("/blog/archive/{start}/{end}")
*/
public function archive(\DateTime $start, \DateTime $end)
{
}
默认情况下,接受 DateTime
构造函数或 unix 时间戳可以解析的任何日期格式。您可以通过选项对给定的输入进行更严格的限制
1 2 3 4 5 6 7 8
/**
* @Route("/blog/archive/{start}/{end}")
* @ParamConverter("start", options={"format": "!Y-m-d"})
* @ParamConverter("end", options={"format": "!Y-m-d"})
*/
public function archive(\DateTime $start, \DateTime $end)
{
}
格式错误的日期(如 2017-21-22
)将返回 404。
创建转换器
所有转换器都必须实现 ParamConverterInterface
1 2 3 4 5 6 7 8 9 10 11
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
interface ParamConverterInterface
{
function apply(Request $request, ParamConverter $configuration);
function supports(ParamConverter $configuration);
}
当 supports()
方法能够转换给定的配置(ParamConverter
实例)时,它必须返回 true
。
ParamConverter
实例具有关于注解的三个信息
name
: 属性名称;class
: 属性类名(可以是表示类名的任何字符串);options
: 选项数组。
每当支持配置时,都会调用 apply()
方法。基于请求属性,它应设置一个名为 $configuration->getName()
的属性,该属性存储一个类为 $configuration->getClass()
的对象。
如果您正在使用服务自动注册和自动配置,那么您就完成了!您的转换器将自动被使用。
您可以按优先级、按名称(属性 "converter")或两者都注册转换器。如果您未指定优先级或名称,则会将转换器添加到转换器堆栈,优先级为 0
。要显式禁用按优先级注册,您必须在标记定义中设置 priority="false"
。
提示
如果您想将服务或附加参数注入到自定义参数转换器中,则优先级不应高于 1
。否则,服务将无法加载。
提示
使用 DoctrineParamConverter
类作为您自己的转换器的模板。