mmoreram/controller-extra-bundle

一些特定的控制器注解

v2.0.3 2017-12-04 14:21 UTC

README

Build Status

此包提供了一组用于 Symfony2 控制器的注解,旨在简化某些对象的创建,并使操作更小、更简洁。

目录

  1. 参考
  2. 实体提供者
  3. 控制器注解
  4. 自定义注解

参考

默认情况下,加载所有注解,但可以通过将 active 参数设置为 false 来完全禁用任何单个注解。

默认值是

controller_extra:
    resolver_priority: -8
    request: current
    paginator:
        active: true
        default_name: paginator
        default_page: 1
        default_limit_per_page: 10
    entity:
        active: true
        default_name: entity
        default_persist: true
        default_mapping_fallback: false
        default_factory_method: create
        default_factory_mapping: true
    form:
        active: true
        default_name: form
    object_manager:
        active: true
        default_name: form
    flush:
        active: true
        default_manager: default
    json_response:
        active: true
        default_status: 200
        default_headers: []
    log:
        active: true
        default_level: info
        default_execute: pre

ResolverEventListener 订阅了 kernel.controller 事件,优先级为 -8。此元素可以通过 resolver_priority 配置值进行配置和自定义。如果您需要获取 ParamConverter 实体,请确保此值小于 0。原因是此监听器必须在 ParamConverter 之一之后始终执行。

实体提供者

在某些注解中,您可以通过多种方式定义一个实体。本章将介绍如何定义它们。

按命名空间

您可以使用其命名空间定义一个实体。只需执行一个简单的 new() 操作。

/**
 * Simple controller method
 *
 * @SomeAnnotation(
 *      class = "Mmoreram\CustomBundle\Entity\MyEntity",
 * )
 */
public function indexAction()
{
}

按 doctrine 快捷方式

您可以使用 Doctrine 快捷方式定义一个实体。使用此格式时,应确保您的实体遵循 Symfony Bundle 标准,并且实体位于 Entity/ 文件夹下。

/**
 * Simple controller method
 *
 * @SomeAnnotation(
 *      class = "MmoreramCustomBundle:MyEntity",
 * )
 */
public function indexAction()
{
}

按参数

您可以使用简单的配置参数定义一个实体。一些项目使用参数来定义所有实体命名空间(以允许覆盖)。如果您使用参数定义实体,此包将尝试通过简单的 new() 直接访问容器参数包来实例化它。

parameters:

    #
    # Entities
    #
    my.bundle.entity.myentity: Mmoreram\CustomBundle\Entity\MyEntity
/**
 * Simple controller method
 *
 * @SomeAnnotation(
 *      class = "my.bundle.entity.myentity",
 * )
 */
public function indexAction()
{
}

控制器注解

此包提供了一组减少但实用的注解,用于您的控制器操作。

@CreatePaginator

根据请求和配置创建一个 Doctrine Paginator 对象。此注解仅将一个新 Doctrine\ORM\Tools\Pagination\Pagination 实例注入到控制器中,该实例可用于迭代。

您可以通过覆盖配置文件 config.yml 中的 active 标志来启用/禁用此包。

controller_extra:
    pagination:
        active: true

默认情况下,如果未设置 name 选项,生成的对象将放置在名为 $paginator 的参数中。您可以使用配置中的 default_name 配置此行为。

此注解可以通过以下部分进行配置

分页器实体

要创建新的分页对象,您需要引用一个现有的实体。您可以阅读 实体提供者 部分,了解您可以定义它的所有可用格式。

<?php

use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 * )
 */
public function indexAction(Paginator $paginator)
{
}

分页器页面

您需要指定分页器注解的页面以获取。默认情况下,如果没有指定,此包将使用配置中定义的默认值。您可以在 config.yml 中覆盖它。

controller_extra:
    pagination:
        default_page: 1

您可以使用 ~value~ 格式引用现有的请求属性,使用 ?field? 格式引用任何 $_GET 元素,或使用 #field# 格式引用任何 $_POST 元素。

您可以通过配置配置项中的请求值,在主请求或当前请求之间进行选择,以访问其属性。

use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * This Controller matches pattern /myroute/paginate/{foo}
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      page = "~foo~"
 * )
 */
public function indexAction(Paginator $paginator)
{
}

或者您可以将页面硬编码以使用。

use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * This Controller matches pattern /myroute/paginate/
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      page = 1
 * )
 */
public function indexAction(Paginator $paginator)
{
}

分页器限制

您需要指定分页器注解的限制以获取数据。默认情况下,如果没有指定,此包将使用配置中定义的默认值。您可以在 config.yml 中覆盖它。

controller_extra:
    pagination:
        default_limit_per_page: 10

您可以使用 ~value~ 格式引用现有的请求属性,使用 ?field? 格式引用任何 $_GET 元素,或使用 #field# 格式引用任何 $_POST 元素。

use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * This Controller matches pattern /myroute/paginate/{foo}/{limit}
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      page = "~foo~",
 *      limit = "~limit~"
 * )
 */
public function indexAction(Paginator $paginator)
{
}

或者您可以将页面硬编码以使用。

use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * This Controller matches pattern /myroute/paginate/
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      page = 1,
 *      limit = 10
 * )
 */
public function indexAction(Paginator $paginator)
{
}

分页器排序

您可以通过定义要按顺序排序的字段和所需的方向来排序分页。必须将 orderBy 部分定义为数组数组,并且每个数组应包含以下位置

  • 第一个位置:实体别名(主要对象设置为 x
  • 第二个位置:实体字段
  • 第三个位置:方向
  • 第四个位置:自定义方向映射 (可选)
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      orderBy = {
 *          {"x", "createdAt", "ASC"},
 *          {"x", "updatedAt", "DESC"},
 *          {"x", "id", 1, {
 *              0 => "ASC",
 *              1 => "DESC",
 *          }},
 *      }
 * )
 */
public function indexAction(Paginator $paginator)
{
}

使用第三个和第四个值,您可以定义一个映射,将您自己的方向命名与DQL方向相匹配。DQL命名只接受 ASC 表示上升和 DESC 表示下降。

当您需要将URL格式与DQL格式相匹配时,这非常有用。您可以使用 ~value~ 格式引用现有请求属性,使用格式 ?field? 引用任何 $_GET 元素,或使用格式 #field# 引用任何 $_POST

use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * This Controller matches pattern /myroute/paginate/order/{field}/{direction}
 *
 * For example, some matchings...
 *
 * /myroute/paginate/order/id/1 -> ORDER BY id DESC
 * /myroute/paginate/order/enabled/0 - ORDER BY enabled ASC
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      orderBy = {
 *          {"x", "createdAt", "ASC"},
 *          {"x", "updatedAt", "DESC"},
 *          {"x", "~field~", ~direction~, {
 *              0 => "ASC",
 *              1 => "DESC",
 *          }},
 *      }
 * )
 */
public function indexAction(Paginator $paginator)
{
}

定义的顺序将改变DQL查询的顺序。

分页器条件

您可以在分页器中定义一些 WHERE 语句。必须将 wheres 部分定义为数组数组,并且每个数组应包含以下位置

  • 第一个位置:实体别名(主要对象设置为 x
  • 第二个位置:实体字段
  • 第三个位置:运算符 =, <=, >, LIKE...
  • 第四个位置:要比较的值
  • 第五个位置:是否为过滤器。默认为 false
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      wheres = {
 *          {"x", "enabled", "=", true},
 *          {"x", "age", ">", 18},
 *          {"x", "name", "LIKE", "Eferv%"},
 *      }
 * )
 */
public function indexAction(Paginator $paginator)
{
}

您可以使用 ~value~ 格式引用现有的请求属性,使用 ?field? 格式引用任何 $_GET 元素,或使用 #field# 格式引用任何 $_POST 元素。

use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * This Controller matches pattern /myroute/{field}
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      wheres = {
 *          {"x", "name", "LIKE", "~field~"},
 *      }
 * )
 */
public function indexAction(Paginator $paginator)
{
}

您还可以使用此功能进行可选过滤,将最后一个位置设置为 true。在这种情况下,如果未找到过滤器值,则将忽略此行。

use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * This Controller matches pattern /myroute?query=name%
 * This Controller matches pattern /myroute as well
 *
 * In both cases this will work. In the first case we will apply the where line
 * in the paginator. In the second case, we wont.
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      wheres = {
 *          {"x", "name", "LIKE", "?query?", true},
 *      }
 * )
 */
public function indexAction(Paginator $paginator)
{
}

分页器非空

您还可以定义一些字段为非空。与 wheres 部分相同,但特定于 NULL 赋值。必须将 notNulls 部分定义为数组数组,并且每个数组应包含以下位置

  • 第一个位置:对象(主要对象设置为 x
  • 第二个位置:字段
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      notNulls = {
 *          {"x", "enabled"},
 *          {"x", "deleted"},
 *      }
 * )
 */
public function indexAction(Paginator $paginator)
{
}

分页器左连接

您可以在本节中进行一些左连接。必须将 leftJoins 部分定义为数组数组,其中每个数组可以包含以下字段

  • 第一个位置:实体别名(主要对象设置为 x
  • 第二个位置:实体关系(地址)
  • 第三个位置:关系标识符(a)
  • 第四个位置:如果为 true,则此关系将添加到选择组中。否则,直到请求才会加载该关系(可选
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      leftJoins = {
 *          {"x", "User", "u", true},
 *          {"x", "Address", "a", true},
 *          {"x", "Cart", "c"},
 *      }
 * )
 */
public function indexAction(Paginator $paginator)
{
}

分页器内连接

您可以在本节中进行一些内连接。必须将 innerJoins 部分定义为数组数组,其中每个数组可以包含以下字段

  • 第一个位置:实体别名(x)
  • 第二个位置:实体关系(地址)
  • 第三个位置:关系标识符(a)
  • 第四个位置:如果为 true,则此关系将添加到选择组中。否则,直到请求才会加载该关系(可选
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      innerJoins = {
 *          {"x", "User", "u", true},
 *          {"x", "Address", "a", true},
 *          {"x", "Cart", "c"},
 *      }
 * )
 */
public function indexAction(Paginator $paginator)
{
}

分页器属性

此注解的一个不错之处在于,您还可以将一个 Mmoreram\ControllerExtraBundle\ValueObject\PaginatorAttributes 实例注入到您的控制器中,该实例包含有关分页的一些有趣信息。

  • currentPage : 当前获取的页面
  • totalElements : 根据您的标准给出的总元素数。如果您在配置中未定义任何标准,此值将显示特定实体的所有元素。
  • totalPages : 根据标准可以获取的总页数。
  • limitPerPage: 每页中元素的最大数量。

要注入此对象,您需要定义 "attributes" 注解字段与方法参数名称。

use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
use Mmoreram\ControllerExtraBundle\ValueObject\PaginatorAttributes;

/**
 * Simple controller method
 *
 * This Controller matches pattern /myroute/paginate/
 *
 * @CreatePaginator(
 *      attributes = "paginatorAttributes",
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      page = 1,
 *      limit = 10
 * )
 */
public function indexAction(
    Paginator $paginator,
    PaginatorAttributes $paginatorAttributes
)
{
    $currentPage = $paginatorAttributes->getCurrentPage();
    $totalElements = $paginatorAttributes->getTotalElements();
    $totalPages = $paginatorAttributes->getTotalPages();
    $limitPerPage = $paginatorAttributes->getLimitPerPage();

}

分页器示例

这是一个完整示例及其 DQL 解析

use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;

/**
 * Simple controller method
 *
 * This Controller matches pattern /paginate/nb/{limit}/{page}
 *
 * Where:
 *
 * * limit = 10
 * * page = 1
 *
 * @CreatePaginator(
 *      entityNamespace = "ControllerExtraBundle:Fake",
 *      page = "~page~",
 *      limit = "~limit~",
 *      orderBy = {
 *          { "x", "createdAt", "ASC" },
 *          { "x", "updatedAt", "DESC" },
 *          { "x", "id", "0", {
 *              "1" = "ASC",
 *              "2" = "DESC",
 *          }}
 *      },
 *      wheres = {
 *          { "x", "enabled" , "=", true }
 *      },
 *      leftJoins = {
 *          { "x", "relation", "r" },
 *          { "x", "relation2", "r2" },
 *          { "x", "relation5", "r5", true },
 *      },
 *      innerJoins = {
 *          { "x", "relation3", "r3" },
 *          { "x", "relation4", "r4", true },
 *      },
 *      notNulls = {
 *          {"x", "address1"},
 *          {"x", "address2"},
 *      }
 * )
 */
public function indexAction(Paginator $paginator)
{
}

此注解生成的 DQL 为

    SELECT x, r4, r5
    FROM Mmoreram\\ControllerExtraBundle\\Tests\\FakeBundle\\Entity\\Fake x

    INNER JOIN x.relation3 r3
    INNER JOIN x.relation4 r4

    LEFT JOIN x.relation r
    LEFT JOIN x.relation2 r2
    LEFT JOIN x.relation5 r5

    WHERE enabled = ?where0
    AND x.address1 IS NOT NULL
    AND x.address2 IS NOT NULL

    ORDER BY createdAt ASC, id ASC

PagerFanta 扩展插件

如果您需要它,此注解可以创建一个 PagerFanta 实例。您只需定义您的参数,注解解析器将使用 Pagerfanta 对象实例包装您的分页器。

use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
use Pagerfanta\Pagerfanta;

/**
 * Simple controller method
 *
 * This Controller matches pattern /myroute/paginate/
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      page = 1,
 *      limit = 10
 * )
 */
public function indexAction(Pagerfanta $paginator)
{
}

KNPPaginator 扩展插件

如果您需要它,此注解可以创建一个 KNPPaginator 实例。您只需定义您的参数,注解解析器将使用 KNPPaginator 对象实例包装您的分页器。

use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
use Knp\Component\Pager\Pagination\PaginationInterface;

/**
 * Simple controller method
 *
 * This Controller matches pattern /myroute/paginate/
 *
 * @CreatePaginator(
 *      entityNamespace = "MmoreramCustomBundle:User",
 *      page = 1,
 *      limit = 10
 * )
 */
public function indexAction(PaginationInterface $paginator)
{
}

@LoadEntity

从数据库中加载实体,或创建一个新的实体。

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Entity;
use Mmoreram\ControllerExtraBundle\Entity\User;

/**
 * Simple controller method
 *
 * @Entity(
 *      namespace = "MmoreramCustomBundle:User",
 *      name  = "user"
 * )
 */
public function indexAction(User $user)
{
}

默认情况下,如果没有设置 name 选项,生成的对象将被放置在一个名为 $entity 的参数中。这个行为可以通过配置中的 default_name 来配置。

您还可以在实体注解中使用设置器。这意味着您可以简单地通过请求属性调用实体设置器。

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Entity;
use Mmoreram\ControllerExtraBundle\Entity\Address;
use Mmoreram\ControllerExtraBundle\Entity\User;

/**
 * Simple controller method
 *
 * @Entity(
 *      namespace = "MmoreramCustomBundle:Address",
 *      name  = "address"
 * )
 * @Entity(
 *      namespace = "MmoreramCustomBundle:User",
 *      name  = "user",
 *      setters = {
 *          "setAddress": "address"
 *      }
 * )
 */
public function indexAction(Address $address, User $user)
{
}

当构建 User 实例时,使用新的 Address 实例作为参数调用 setAddress 方法。

新实体仅通过简单的 new() 创建,因此它们不会被持久化。默认情况下,它们将使用配置的管理器进行持久化,但您可以使用 persist 选项禁用此功能。

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Entity;
use Mmoreram\ControllerExtraBundle\Entity\User;

/**
 * Simple controller method
 *
 * @Entity(
 *      namespace = "MmoreramCustomBundle:User",
 *      name  = "user",
 *      persist = false
 * )
 */
public function indexAction(User $user)
{
}

实体映射

当您定义一个新的实体注解时,您还可以请求给定的映射实体。这意味着如果定义了一个映射,此包将尝试请求满足条件的映射实例。

映射的键表示映射字段的名称,值表示它们的期望值。请记住,您可以通过使用格式 ~field~ 来引用任何请求属性,使用格式 ?field? 来引用任何 $_GET 元素,或使用格式 #field# 来引用任何 $_POST

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Entity;
use Mmoreram\ControllerExtraBundle\Entity\User;

/**
 * Simple controller method
 *
 * This Controller matches pattern /user/edit/{id}/{username}
 *
 * @Entity(
 *      namespace = "MmoreramCustomBundle:User",
 *      name  = "user",
 *      mapping = {
 *          "id": "~id~",
 *          "username": "~username~"
 *      }
 * )
 */
public function indexAction(User $user)
{
}

在这种情况下,您将尝试获取具有传递的 id 的映射用户实例。如果定义了映射并且找到了任何实体,将抛出一个新的 `EntityNotFoundException`。

实体映射回退

那么,如果找不到一个或多个映射引用会怎样呢?例如,您正在尝试从您的路由映射 {id} 参数,但此参数甚至没有定义。这时会发生什么?好吧,那时您可以假设您想通过使用 mappingFallback 来传递一个新的实体实例。

默认情况下,如果没有设置 mapping_fallback 选项,将使用配置中定义的 default_mapping_fallback 参数值。默认情况下,此值是 false

不要与您正在数据库中查找实体的情况混淆,所有映射引用都已解析,但实体未找到。在这种情况下,Doctrine 将抛出一个常见的 "EntityNotFound" 异常。

让我们看看一个例子。因为我们已经启用了映射回退,并且因为映射定义不匹配分配的路由,所以我们将返回一个新的空 User 实体。

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Entity;
use Mmoreram\ControllerExtraBundle\Entity\User;

/**
 * Simple controller method
 *
 * This Controller matches pattern /user/edit/{id}
 *
 * @LoadEntity(
 *      namespace = "MmoreramCustomBundle:User",
 *      name  = "user",
 *      mapping = {
 *          "id": "~id~",
 *          "username": "~nonexisting~"
 *      },
 *      mappingFallback = true
 * )
 */
public function indexAction(User $user)
{
    // $user->getId() === null
}

实体仓库

默认情况下,Doctrine 实体管理器为每个实体提供正确的存储库(不是默认的,而是特定的一个)。尽管如此,您可以通过使用存储库配置来定义在注解中使用的自定义存储库。

/**
 * Simple controller method
 *
 * @CreateEntity(
 *      namespace = "MmoreramCustomBundle:User",
 *      mapping = {
 *          "id": "~id~",
 *          "username": "~username~"
 *      }
 *      repository = {
 *          "class" = "Mmoreram\CustomBundle\Repository\AnotherRepository",
 *      },
 * )
 */
public function indexAction(User $user)
{
}

默认情况下,将始终使用 findOneBy 方法,除非您定义了另一个方法。

/**
 * Simple controller method
 *
 * @CreateEntity(
 *      namespace = "MmoreramCustomBundle:User",
 *      mapping = {
 *          "id": "~id~",
 *          "username": "~username~"
 *      }
 *      repository = {
 *          "class" = "Mmoreram\CustomBundle\Repository\AnotherRepository",
 *          "method" = "find",
 *      },
 * )
 */
public function indexAction(User $user)
{
}

实体工厂

当注解认为必须创建一个新实体,因为没有提供映射信息,或者因为已激活映射回退时,默认情况下将使用 namespace 值创建一个新的实例。

此配置块有三个位置

  • class - 工厂类
  • method - 获取对象时使用的方法
  • static - 方法是静态的

您可以使用简单的命名空间定义工厂。

/**
 * Simple controller method
 *
 * @CreateEntity(
 *      namespace = "MmoreramCustomBundle:User",
 *      factory = {
 *          "class" = "Mmoreram\CustomBundle\Factory\UserFactory",
 *          "method" = "create",
 *          "static" = true,
 *      },
 * )
 */
public function indexAction(User $user)
{
}

如果您想将您的工厂定义为服务,并具有覆盖命名空间的可能性,您只需定义服务名称。所有其他选项都具有相同的行为。

parameters:

    #
    # Factories
    #
    my.bundle.factory.user_factory: Mmoreram\CustomBundle\Factory\UserFactory
/**
 * Simple controller method
 *
 * @CreateEntity(
 *      class = {
 *          "factory" = my.bundle.factory.user_factory,
 *          "method" = "create",
 *          "static" = true,
 *      },
 * )
 */
public function indexAction(User $user)
{
}

如果您没有定义 method,将使用默认值。您可以通过在 config.yml 中定义新的来覆盖此默认值。与 static 值相同。

controller_extra:
    entity:
        default_factory_method: create
        default_factory_static: true

@CreateForm

为您的控制器操作提供表单注入。此注解只需要定义一个名称,其中您必须定义放置表单的命名空间。

<?php

use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Symfony\Component\Form\AbstractType;

/**
 * Simple controller method
 *
 * @CreateForm(
 *      class = "\Mmoreram\CustomBundle\Form\Type\UserType",
 *      name  = "userType"
 * )
 */
public function indexAction(AbstractType $userType)
{
}

默认情况下,如果没有设置 name 选项,生成的对象将被放置在一个名为 $form 的参数中。此行为可以通过配置中的 default_name 来配置。

您不能仅使用命名空间定义您的类型位置,在这种情况下,将创建一个新的 AbstractType 元素,但您也可以使用服务别名来定义它,在这种情况下,此包将使用 Symfony DI 返回一个实例。

<?php

use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Symfony\Component\Form\AbstractType;

/**
 * Simple controller method
 *
 * @CreateForm(
 *      class = "user_type",
 *      name  = "userType"
 * )
 */
public function indexAction(AbstractType $userType)
{
}

此注解允许您不仅创建FormType实例,还允许您注入Form对象或FormView对象

要注入Form对象,您只需将方法值转换为该类型。

<?php

use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Symfony\Component\Form\Form;

/**
 * Simple controller method
 *
 * @CreateForm(
 *      class = "user_type",
 *      name  = "userForm"
 * )
 */
public function indexAction(Form $userForm)
{
}

您还可以使用[SensioFrameworkExtraBundle][1]的[ParamConverter][2],通过之前创建的实体创建Form对象。您可以使用entity参数定义此实体。

<?php

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\Form\Form;

use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Mmoreram\ControllerExtraBundle\Entity\User;

/**
 * Simple controller method
 *
 * @Route(
 *      path = "/user/{id}",
 *      name = "view_user"
 * )
 * @ParamConverter("user", class="MmoreramCustomBundle:User")
 * @CreateForm(
 *      class  = "user_type",
 *      entity = "user"
 *      name   = "userForm",
 * )
 */
public function indexAction(User $user, Form $userForm)
{
}

要处理当前请求,可以将handleRequest设置为true。默认情况下,此值设置为false

<?php

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\Form\Form;

use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Mmoreram\ControllerExtraBundle\Entity\User;

/**
 * Simple controller method
 *
 * @Route(
 *      path = "/user/{id}",
 *      name = "view_user"
 * )
 * @ParamConverter("user", class="MmoreramCustomBundle:User")
 * @CreateForm(
 *      class         = "user_type",
 *      entity        = "user"
 *      handleRequest = true,
 *      name          = "userForm",
 * )
 */
public function indexAction(User $user, Form $userForm)
{
}

您还可以将validate设置作为方法参数添加,如果表单有效。注解将$form->isValid()的结果放置在指定的方法参数中。

<?php

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\Form\Form;

use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Mmoreram\ControllerExtraBundle\Entity\User;

/**
 * Simple controller method
 *
 * @Route(
 *      path = "/user/{id}",
 *      name = "view_user"
 * )
 * @ParamConverter("user", class="MmoreramCustomBundle:User")
 * @CreateForm(
 *      class         = "user_type",
 *      entity        = "user"
 *      handleRequest = true,
 *      name          = "userForm",
 *      validate      = "isValid",
 * )
 */
public function indexAction(User $user, Form $userForm, $isValid)
{
}

要注入FormView对象,您只需将方法变量转换为该类型。

<?php

use Symfony\Component\Form\FormView;

use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;

/**
 * Simple controller method
 *
 * @CreateForm(
 *      class = "user_type",
 *      name  = "userFormView"
 * )
 */
public function indexAction(FormView $userFormView)
{
}

@Flush

Flush注解允许您使用kernel.response事件在请求结束时刷新entityManager

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Flush;

/**
 * Simple controller method
 *
 * @Flush
 */
public function indexAction()
{
}

如果未指定其他内容,则默认Doctrine Manager将与此注解一起刷新。您可以在您的config.yml文件中覆盖默认Manager。

controller_extra:
    flush:
        default_manager: my_custom_manager

您还可以在定义manager值的每个Flush注解实例中覆盖此值

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Flush;

/**
 * Simple controller method
 *
 * @Flush(
 *      manager = "my_own_manager"
 * )
 */
public function indexAction()
{
}

如果您想更改所有注解实例中的默认Manager,您应在您的config.yml文件中覆盖bundle参数。

controller_extra:
    flush:
        default_manager: my_own_manager

如果设置了任何参数,则注解将刷新所有内容。如果您只需刷新一个或多个实体,您可以明确定义必须刷新的实体。

<?php

use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;

use Mmoreram\ControllerExtraBundle\Annotation\Flush;
use Mmoreram\ControllerExtraBundle\Entity\User;

/**
 * Simple controller method
 *
 * @ParamConverter("user", class="MmoreramCustomBundle:User")
 * @Flush(
 *      entity = "user"
 * )
 */
public function indexAction(User $user)
{
}

您还可以定义要刷新的一组实体

<?php

use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;

use Mmoreram\ControllerExtraBundle\Annotation\Flush;
use Mmoreram\ControllerExtraBundle\Entity\Address;
use Mmoreram\ControllerExtraBundle\Entity\User;

/**
 * Simple controller method
 *
 * @ParamConverter("user", class="MmoreramCustomBundle:User")
 * @ParamConverter("address", class="MmoreramCustomBundle:Address")
 * @Flush(
 *      entity = {
 *          "user", 
 *          "address"
 *      }
 * )
 */
public function indexAction(User $user, Address $address)
{
}

如果在同一操作中定义了多个@Mmoreram\Flush,则最后一个实例将覆盖之前的实例。但是,只需定义一个实例。

@ToJsonResponse

JsonResponse注解允许您根据简单的控制器返回值创建Symfony\Component\HttpFoundation\JsonResponse对象

<?php

use Mmoreram\ControllerExtraBundle\Annotation\ToJsonResponse;

/**
 * Simple controller method
 *
 * @ToJsonResponse
 */
public function indexAction(User $user, Address $address)
{
    return array(
        'This is my response'
    );
}

默认情况下,JsonResponse使用默认的statusheaders创建,这些默认值在bundle参数中定义。您可以在您的config.yml文件中覆盖它们。

controller_extra:
    json_response:
        default_status: 403
        default_headers:
            "User-Agent": "Googlebot/2.1"

您还可以在每个@JsonResponse注解中覆盖这些值。

<?php

use Mmoreram\ControllerExtraBundle\Annotation\ToJsonResponse;

/**
 * Simple controller method
 *
 * @ToJsonResponse(
 *      status = 403,
 *      headers = {
 *          "User-Agent": "Googlebot/2.1"
 *      }
 * )
 */
public function indexAction(User $user, Address $address)
{
    return array(
        'This is my response'
    );
}

如果返回异常,则默认将响应状态设置为500,并将异常消息作为响应返回。

状态 500 内部服务器错误

{
    message : 'Exception message'
}

如果我们使用HttpExceptionInterface,则将异常状态代码用作状态代码。如果我们引发此异常

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

...

return new NotFoundHttpException('Resource not found');

我们将收到此响应

状态 404 未找到

{
    message : 'Resource not found'
}

如果在注解(例如Entity注解)上引发异常,请记住在开始或至少在可能引发异常的任何注解之前添加JsonResponse注解。

如果在同一操作中定义了多个@Mmoreram\JsonResponse,则最后一个实例将覆盖之前的实例。但是,只需定义一个实例。

@Log

Log注解允许您在控制器操作执行前后记录任何纯文本消息

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Log;

/**
 * Simple controller method
 *
 * @Log("Executing index Action")
 */
public function indexAction()
{
}

您可以定义消息的级别。您可以定义默认级别,如果未指定,则覆盖您的config.yml文件中的默认值。

controller_extra:
    log:
        default_level: warning

每个注解实例都可以使用level字段覆盖此值。

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Log;

/**
 * Simple controller method
 *
 * @Log(
 *      value   = "Executing index Action",
 *      level   = @Log::LVL_WARNING
 * )
 */
public function indexAction()
{
}

可以使用多个级别,如[Psr\Log\LoggerInterface][6]接口中定义的

  • @Mmoreram\Log::LVL_EMERG
  • @Mmoreram\Log::LVL_CRIT
  • @Mmoreram\Log::LVL_ERR
  • @Mmoreram\Log::LVL_WARN
  • @Mmoreram\Log::LVL_NOTICE
  • @Mmoreram\Log::LVL_INFO
  • @Mmoreram\Log::LVL_DEBUG
  • @Mmoreram\Log::LVL_LOG

您还可以定义日志的执行。您可以定义默认执行,如果未指定,则覆盖您的config.yml文件中的默认值。

controller_extra:
    log:
        default_execute: pre

每个注解实例都可以使用level字段覆盖此值。

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Log;

/**
 * Simple controller method
 *
 * @Log(
 *      value   = "Executing index Action",
 *      execute = @Log::EXEC_POST
 * )
 */
public function indexAction()
{
}

可以使用多个执行,

  • @Mmoreram\Log::EXEC_PRE - 在控制器执行前记录
  • @Mmoreram\Log::EXEC_POST - 在控制器执行后记录
  • @Mmoreram\Log::EXEC_BOTH - 两者都记录

@Get

Get注解允许您从请求查询字符串中获取任何参数。

对于类似GET的请求

GET /my-page?foo=bar HTTP/1.1

您可以使用GET注解简单获取foo变量

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Get;

/**
 * Simple controller method
 *
 * @Get(
 *     path = "foo"
 * )
 */
public function indexAction($foo)
{
    // Use the foo var
}

您还可以自定义变量名称和默认值,以防变量未在查询字符串中发送。

对于类似GET的请求

GET /my-page HTTP/1.1

并且这个注释

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Get;

/**
 * Simple controller method
 *
 * @Get(
 *     path = "foo",
 *     name = "varName",
 *     default = 'bar',
 * )
 */
public function indexAction($varName)
{
    // This would print 'bar'
    echo $varName;
}

@Post

后置注释允许您从POST请求体中获取任何参数。

对于类似下面的POST请求

POST /my-page HTTP/1.1
foo=bar

您可以通过使用POST注释简单地获取foo变量

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Post;

/**
 * Simple controller method
 *
 * @Post(
 *     path = "foo"
 * )
 */
public function indexAction($foo)
{
    // Use the foo var
}

您还可以自定义变量名称和默认值,以防变量未在查询字符串中发送。

对于类似下面的POST请求

POST /my-page HTTP/1.1

并且这个注释

<?php

use Mmoreram\ControllerExtraBundle\Annotation\Post;

/**
 * Simple controller method
 *
 * @Post(
 *     path = "foo",
 *     name = "varName",
 *     default = 'bar',
 * )
 */
public function indexAction($varName)
{
    // This would print 'bar'
    echo $varName;
}

自定义注解

使用这个包,您可以非常容易地创建自己的控制器注释。

注解

注释对象。您需要定义您自定义注释将包含的字段。必须扩展Mmoreram\ControllerExtraBundle\Annotation\Annotation抽象类。

<?php

namespace My\Bundle\Annotation;

use Mmoreram\ControllerExtraBundle\Annotation\Annotation;

/**
 * Entity annotation driver
 *
 * @Annotation
 * @Target({"METHOD"})
 */
final class MyCustomAnnotation extends Annotation
{
    /**
     * @var string
     *
     * Dummy field
     */
    public $field;
    
    /**
     * Get Dummy field
     *
     * @return string Dummy field
     */
    public function getField()
    {
        return $this->field;
    }
}

解析器

一旦您定义了自己的注释,您就必须确定这个注释在控制器中如何工作。您可以使用解析器来管理这一点。必须扩展Mmoreram\ControllerExtraBundle\Resolver\AnnotationResolver;抽象类。

<?php

namespace My\Bundle\Resolver;

use Symfony\Component\HttpFoundation\Request;

use Mmoreram\ControllerExtraBundle\Resolver\AnnotationResolver;
use Mmoreram\ControllerExtraBundle\Annotation\Annotation;

/**
 * MyCustomAnnotation Resolver
 */
class MyCustomAnnotationResolver extends AnnotationResolver
{
    /**
     * Specific annotation evaluation.
     *
     * This method must be implemented in every single EventListener
     * with specific logic
     *
     * All method code will executed only if specific active flag is true
     *
     * @param Request          $request
     * @param Annotation       $annotation
     * @param ReflectionMethod $method
     */
    public function evaluateAnnotation(
        Request $request,
        Annotation $annotation,
        ReflectionMethod $method
    )
    {
        /**
         * You can now manage your annotation.
         * You can access to its fields using public methods.
         * 
         * Annotation fields can be public and can be acceded directly,
         * but is better for testing to use getters; they can be mocked.
         */
        $field = $annotation->getField();
        
        /**
         * You can also access to existing method parameters.
         * 
         * Available parameters are:
         * 
         * # ParamConverter parameters ( See `resolver_priority` config value )
         * # All method defined parameters, included Request object if is set.
         */
        $entity = $request->attributes->get('entity');
        
        /**
         * And you can now place new elements in the controller action.
         * In this example we are creating new method parameter
         * called $myNewField with some value
         */
        $request->attributes->set(
            'myNewField',
            new $field()
        );
        
        return $this;
    }

}

这个类将被定义为一个服务,所以这个方法是在执行当前控制器之前计算的。您还可以订阅一些内核事件并执行您需要做的任何事情(您可以在Mmoreram\ControllerExtraBundle\Resolver\LogAnnotationResolver中查看一些示例)。

定义

一旦解析器完成,我们需要将我们的服务定义为注释解析器。我们将使用一个自定义的tag

parameters:
    #
    # Resolvers
    #
    my.bundle.resolver.my_custom_annotation_resolver.class: My\Bundle\Resolver\MyCustomAnnotationResolver

services:
    #
    # Resolvers
    #
    my.bundle.resolver.my_custom_annotation_resolver:
        class: %my.bundle.resolver.my_custom_annotation_resolver.class%
        tags:
            - { name: controller_extra.annotation }

注册

我们需要在我们的应用程序中注册我们的注释。我们可以在bundle.php文件的boot()方法中完成这一点。

<?php

namespace My\Bundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Doctrine\Common\Annotations\AnnotationRegistry;

/**
 * MyBundle
 */
class ControllerExtraBundle extends Bundle
{

    /**
     * Boots the Bundle.
     */
    public function boot()
    {
        $kernel = $this->container->get('kernel');

        AnnotationRegistry::registerFile($kernel
            ->locateResource("@MyBundle/Annotation/MyCustomAnnotation.php")
        );
    }
}

就这样!我们现在可以在我们的项目控制器中使用我们的自定义注释了。