Пакет Symfony и Wildurand/Hateoas — нет ссылок на ответ JSON

Я использую связку FOSRest и Willdurand/Hateoas. Я использую примеры из https://github.com/willdurand/Hateoas#configuring-links

но в ответе JSON нет поля «ссылки».

/**
 * Users
 *
 * @ORM\Table(name="users")
 * @ORM\Entity
 * @Serializer\ExclusionPolicy("ALL")
 * @Hateoas\Relation("self", href="expr('/users' ~ object.getId())")
 */
class User
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @Serializer\Groups({"Default", "Deserialize"})
     * @Serializer\Expose()
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=30)
     * @Assert\NotBlank()
     * @Assert\Length(max="30", min="5")
     * @Serializer\Groups({"Default", "Deserialize"})
     * @Serializer\Expose()
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="email", type="string", length=30)
     * @Assert\NotBlank()
     * @Assert\Email()
     * @Assert\Length(max="30")
     * @Serializer\Groups({"Default", "Deserialize"})
     * @Serializer\Expose()
     */
    private $email;

    /**
     * @var string
     *
     * @ORM\Column(name="username", type="string", length=15)
     * @Assert\NotBlank()
     * @Assert\Length(max="15", min="3")
     * @Serializer\Groups({"Default", "Deserialize"})
     * @Serializer\Expose()
     */
    private $username;

    /**
     * @var string
     *
     * @ORM\Column(name="password", type="string", length=32)
     * @Assert\NotBlank()
     */
    private $password;

    /**
     * @var string
     *
     * @ORM\Column(name="active", type="boolean", length=32)
     * @Serializer\Groups({"Default", "Deserialize"})
     * @Serializer\Expose()
     */
    private $active = true;

    /**
     * @var ArrayCollection
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Role", inversedBy="user")
     * @Serializer\Expose()
     */
    private $roles;

    public function __construct()
    {
        $this->roles = new ArrayCollection();
    }

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @return string
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * @param string $email
     */
    public function setEmail($email)
    {
        $this->email = $email;
    }

    /**
     * @return string
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * @param string $username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

    /**
     * @return string
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * @param string $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * @return string
     */
    public function getActive()
    {
        return $this->active;
    }

    /**
     * @param string $active
     */
    public function setActive($active)
    {
        $this->active = $active;
    }

    /**
     * @return Collection
     */
    public function getRoles()
    {
        return $this->roles;
    }

    /**
     * @param ArrayCollection $roles
     */
    public function setRoles($roles)
    {
        $this->roles = $roles;
    }

}

В основном я хочу показать ссылку на сущность Ролей, но, возможно, проще выяснить, что вызвало проблему даже для ссылки SELF, а затем пойти дальше.

Вот конфигурация

fos_rest:
    routing_loader:
        default_format: json
        include_format: false
    view:
        view_response_listener: 'force'
    body_converter:
        enabled: true
        validate: true
        validation_errors_argument: validationErrors
    param_fetcher_listener: true
    exception:
        enabled: true
        exception_controller: 'AppBundle\Controller\ExceptionController::showAction'
    serializer:
        groups: ['Default']

sensio_framework_extra:
    view:
        annotations: true
    request:
        converters: true

Эта конфигурация хороша тем, что все конечные точки работают нормально, за исключением отсутствия ссылки.

На данный момент я получаю этот ответ на запрос GET

[
  {
    "id": 1,
    "name": "Test name",
    "email": "[email protected]",
    "username": "toskadv",
    "active": true
  },
  {
    "id": 2,
    "name": "Test name",
    "email": "[email protected]",
    "username": "toskadv",
    "active": true
  },
  {
    "id": 3,
    "name": "Test name",
    "email": "[email protected]",
    "username": "toskadv",
    "active": true,
    "roles": {
      "id": 1,
      "name": "ROLE_USER"
    }
  }
]

Также есть данные контроллера.

/**
 * Class UsersController
 * @package AppBundle\Controller
 */
class UsersController extends AbstractController
{
    use ControllerTrait;

    /**
     * @Rest\View()
     */
    public function getUsersAction()
    {
        $users = $this->getDoctrine()->getRepository('AppBundle:User')->findAll();

        return $users;
    }

    /**
     * @param User $user
     * @param ConstraintViolationListInterface $validationErrors
     *
     * @Rest\View(statusCode=201)
     * @ParamConverter("user", converter="fos_rest.request_body")
     * @Rest\NoRoute()
     *
     * @return User $user
     */
    public function postUsersAction(User $user, ConstraintViolationListInterface $validationErrors)
    {
        if (count($validationErrors) > 0) {
            throw new ValidationException($validationErrors);
        }
        $em = $this->getDoctrine()->getManager();
        $role = $em->getRepository('AppBundle:Role')->find(1);
        $user->setRoles($role);

        $em->persist($user);
        $em->flush();

        return $user;
    }

    /**
     * @param User|null $user
     *
     * @Rest\View()
     */
    public function deleteUserAction(User $user = null) {
        if (null === $user) {
            return $this->view(null, 404);
        }

        $em = $this->getDoctrine()->getManager();
        $em->remove($user);
        $em->flush();
    }


    /**
     * @param User $user
     * @return User|\FOS\RestBundle\View\View|null
     *
     * @Rest\View()
     */
    public function getUserAction(User $user)
    {
        if (null === $user) {
            return $this->view(null, 404);
        }

        return $user;
    }

    /**
     * @param Role $role
     * @return Role|\FOS\RestBundle\View\View
     *
     * @Rest\View()
     */
    public function getRoleAction(Role $role)
    {
        if (null === $role) {
            return $this->view(null, 404);
        }

        return $role;
    }

    /**
     * @param User $user
     * @return \Doctrine\Common\Collections\Collection
     *
     * @Rest\View()
     */
    public function getUserRolesAction(User $user)
    {
        return $user->getRoles();
    }

    /**
     * @param User $user
     * @param Role $role
     * @param ConstraintViolationListInterface $validationErrors
     *
     * @Rest\View(statusCode=201)
     * @ParamConverter("role", converter="fos_rest.request_body", options={"deserializationContext"={"groups"={"Deserialize"}}})
     * @Rest\NoRoute()
     *
     * @return Role
     */
    public function postUserRolesAction(User $user, Role $role, ConstraintViolationListInterface $validationErrors)
    {
        if (count($validationErrors) > 0) {
         throw new ValidationException($validationErrors);
        }

        $role->setUser($user);

        $em = $this->getDoctrine()->getManager();
        $user->getRoles()->add($role);

        $em->persist($user);
        $em->flush();

        return $role;
    }
}

person Stevan Tosic    schedule 28.11.2017    source источник
comment
Можете ли вы показать, пожалуйста, когда вы возвращаете ответ от вашего контроллера?   -  person Alessandro Minoccheri    schedule 28.11.2017
comment
@AlessandroMinoccheri Я обновил вопрос, данные контроллера находятся в конце вопроса.   -  person Stevan Tosic    schedule 28.11.2017


Ответы (2)


Проблема в том, что вы возвращаете объект, вам нужно пройти через сериализатор, чтобы получить ссылки ненависти.

Что-то вроде этого:

Я использую jsm_serializer

$serializer = $this->get('jms_serializer');
return new Response(
     $serializer->serialize(
         $users,
         'json',
         SerializationContext::create()->enableMaxDepthChecks()
     ),
     201
);

вместо этого:

return $users;
person Alessandro Minoccheri    schedule 28.11.2017
comment
хм... Я использую пакет FOSRest, который по умолчанию использует jms_serialiser под капотом. Таким образом, действие контроллера возвращает сущность, которая сериализуется с помощью /** @Rest\View() */ Annotation. В моем случае есть какая-то проблема с конфигурацией, неправильно использованная аннотация или что-то третье. Ваше предложенное логическое решение, кстати, мне нужно использовать строго пакет FOSRest без расширения действий контроллеров :) - person Stevan Tosic; 28.11.2017
comment
Я также использую пакет FOSRest, но я управляю ответами таким образом, и он отлично работает, потому что сериализатор управляет ссылками без него, вы не можете получить это значение. - person Alessandro Minoccheri; 28.11.2017
comment
Спасибо за ответ, я просто ищу проблему везде, но не смотрю снова в AppKernel.php. Ваш ответ совершенно правильный. И я благодарен вам за то, что вы потратили время на просмотр кода. - person Stevan Tosic; 30.11.2017
comment
рад помочь вам! - person Alessandro Minoccheri; 30.11.2017
comment
Значит, мы не должны возвращать объекты View из наших контроллеров? - person programmer-man; 31.03.2019

Мне стыдно :/

Проблема была на первом шаге, я просто не включил бандл в AppKernel.php

new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),

это пропущенная строка. Нет никаких оправданий.

person Stevan Tosic    schedule 30.11.2017