$rules = new Assert\Collection([
"items" => new Assert\Required([
new Assert\NotBlank(),
new Assert\Callback(function ($value, ExecutionContextInterface $context) {
$validator = $context->getValidator();
$violations = $validator->validate($value, new Assert\Type('array'));
if ($violations->count() > 0) {
/** @var ConstraintViolationInterface $violation */
foreach ($violations as $violation) {
$context
->buildViolation($violation->getMessage(), $violation->getParameters())
->atPath('items')
->addViolation()
;
}
return;
}
$violations = $validator->validate($value, new Assert\All([
new Assert\Type('array'),
new Assert\Collection([
"id" => new Assert\NotBlank(),
"quantity" => new Assert\NotBlank(),
])
]));
/** @var ConstraintViolationInterface $violation */
foreach ($violations as $violation) {
$context
->buildViolation($violation->getMessage(), $violation->getParameters())
->atPath('items'. $violation->getPropertyPath())
->addViolation()
;
}
}),
]),
]);
$validator = Validation::createValidator();
$data = [
'items' => [
[
'id' => 'id',
'quantity' => 'quantity',
],
],
];
dump($validator->validate($data, $rules)); // Ok
$data = [
'items' => 1,
];
dump($validator->validate($data, $rules)); // Ko
$data = [
'items' => [
[
'id' => 'id',
'quantity' => 'quantity',
],
[
'id2' => 'id',
'quantity2' => 'quantity',
]
],
];
dump($validator->validate($data, $rules)); // Ko
$response = json_decode($make_request->send(
'POST',
$config['url'].'/files',
[
'Content-Type' => 'multipart/form-data',
'Authorization' => $config['token'],
'Accept' => 'application/vnd.api+json',
],
['file' => new \CURLFile($_FILES['file']['tmp_name'], null, $_FILES['file']['name']]),
));
ObjectNormalizer
имеет несколько больше зависимостей для полноценной его работыpublic function submitAction(SerializerInterface $serializer, Request $request)
{
if ($request->isXmlHttpRequest()) {
$data = $request->getContent();
$result = $serializer->deserialize($data, SiteCreateDto::class, 'json');
var_dump($result); exit;
}
}
ObjectNormalizer
не помог, то надо будет написать свой: https://symfony.com/doc/current/serializer/custom_... <?php
namespace App\Controller;
use App\Entity\User;
use App\Service\UserService;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Security;
use Twig\Environment;
interface AdminControllerInterface
{
public function index(): Response;
public function read(string $id): Response;
}
class UserController implements AdminControllerInterface
{
/**
* Security - объект-helper, он содержит в себе AuthorizationCheckerInterface
*
* @var Security
*/
private $security;
/**
* @var ServiceLocator
*/
private $controllerLocators;
public function __construct(Security $security, ServiceLocator $controllerLocators)
{
$this->security = $security;
$this->controllerLocators = $controllerLocators;
}
public function index(): Response
{
$role = $this->getRole();
if (!$this->controllerLocators->has($role)) {
throw new AccessDeniedException();
}
/** @var AdminControllerInterface $controller */
$controller = $this->controllerLocators->get($role);
return $controller->index();
}
public function read(string $id): Response
{
$role = $this->getRole();
if (!$this->controllerLocators->has($role)) {
throw new AccessDeniedException();
}
/** @var AdminControllerInterface $controller */
$controller = $this->controllerLocators->get($role);
return $controller->read($id);
}
private function getRole(): string
{
// Для простоты представим, что роль у пользователя одна. Иначе искать среди ролей
// ROLE_SUPER_ADMIN и ROLE_CUSTOMER_ADMIN и возвращаем с соответствующим приоритетом
return current($this->security->getToken()->getRoles());
}
}
class SuperAdminController implements AdminControllerInterface
{
private $userService;
/**
* @var Environment
*/
private $twig;
public function __construct(
UserService $userService,
Environment $twig
) {
$this->userService = $userService;
$this->twig = $twig;
}
public function index(): Response
{
$users = $this->userService->getAll();
return new Response($this->twig->render('admin/users.html.twig', [
'users' => $users
]));
}
public function read(string $id): Response
{
$user = $this->userService->getById($id);
return new Response($this->twig->render('admin/user.html.twig', [
'user' => $user
]));
}
}
class CustomerAdminController implements AdminControllerInterface
{
private $userService;
/**
* @var Security
*/
private $security;
/**
* @var Environment
*/
private $twig;
public function __construct(
UserService $userService,
Environment $twig
) {
$this->userService = $userService;
$this->twig = $twig;
}
public function index(): Response
{
/** @var User $user */
$user = $this->security->getUser();
$users = $this->userService->findByRoleAndWorkspaceId(
User::ROLE_TEAM_MEMBER,
$user->getWorkspace()->getId()
);
return new Response($this->twig->render('user/users.html.twig', [
'users' => $users
]));
}
public function read(string $id): Response
{
$user = $this->userService->getByIdAndWorkspaceId($id);
return new Response($this->twig->render('user/user.html.twig', [
'user' => $user
]));
}
}
app.admin_controller_locator:
class: Symfony\Component\DependencyInjection\ServiceLocator
arguments:
-
ROLE_SUPER_ADMIN: '@App\Controller\SuperAdminController'
ROLE_CUSTOMER_ADMIN: '@App\Controller\CustomerAdminController'
tags: ['container.service_locator']
UniqueEntity
работает только с Entity. Если хотите валидировать таким образом DTO, то придется написать свой валидатор. За основу можно взять встроенный: https://github.com/symfony/symfony/blob/master/src...services:
_defaults:
autowire: true
autoconfigure: true
bind:
App\Repository\Repository $cachedRepository: '@App\Repository\User\CachedUserRepository'
App\Repository\Repository: '@App\Repository\User\DoctrineUserRepository'
App\Entity\UniqueEntityInterface: '@App\Entity\User'
user_exists_checker:
class: App\ExistsChecker
bind:
App\Repository\Repository: '@App\Repository\User\ApiUserRepository'
user_exists_checker:
class: App\ExistsChecker
bind:
App\Repository\Repository: '@App\Repository\User\ApiUserRepository'
_defaults:
autowire: true
autoconfigure: true
bind:
App\Repository\Repository $cachedRepository: '@App\Repository\User\CachedUserRepository'
App\Repository\Repository $repository
будете использовать App\Repository\Repository $cachedRepository
, то вам придет App\Repository\User\CachedUserRepository
composer.json
для них соответствующий неймспейс в секции autoload
. Но по факту в итоге у вас не получится интегрировать эту библиотеку с symfony (точнее получится, но это будут костыли), т.к. невозможно пробросить свои зависимости в конструктор"autoload": {
"psr-4": { "Longman\\TelegramBot\\Commands\\": "commands/" }
},
/users/123
и /users/123/всечтоугодно
/my
и /my/всечтоугодно
config/routes/annotations.yaml
в config/routes.yml
и отдельно прописать нужный контроллер в конце списка:config/routes.yml
controllers:
resource: ../src/Controller/
type: annotation
page_controller:
resource: ../src/Controller/TextPageController.php
type: annotation
config/routes/dev/*
- тут все стандартно-------------------------- -------- -------- ------ -----------------------------------
Name Method Scheme Host Path
-------------------------- -------- -------- ------ -----------------------------------
_twig_error_test ANY ANY ANY /_error/{code}.{_format}
_wdt ANY ANY ANY /_wdt/{token}
_profiler_home ANY ANY ANY /_profiler/
_profiler_search ANY ANY ANY /_profiler/search
_profiler_search_bar ANY ANY ANY /_profiler/search_bar
_profiler_phpinfo ANY ANY ANY /_profiler/phpinfo
_profiler_search_results ANY ANY ANY /_profiler/{token}/search/results
_profiler_open_file ANY ANY ANY /_profiler/open
_profiler ANY ANY ANY /_profiler/{token}
_profiler_router ANY ANY ANY /_profiler/{token}/router
_profiler_exception ANY ANY ANY /_profiler/{token}/exception
_profiler_exception_css ANY ANY ANY /_profiler/{token}/exception.css
// Controllers
index ANY ANY ANY /
// Page Controller
page_show ANY ANY ANY /{pageSlug}
page_item ANY ANY ANY /{pageSlug}/{parameters}
-------------------------- -------- -------- ------ -----------------------------------
class AccountRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, Account::class);
}
}
class AccountRepository extends \Doctrine\ORM\EntityRepository
{
}
App\Repository\AccountRepository:
arguments:
- 'App\Entity\Account' # Entity class
factory: ['@doctrine', 'getRepository']
If you are building a traditional web application:
composer create-project symfony/website-skeleton my_project
If you are building a microservice, console application or API
composer create-project symfony/skeleton my_project
symfony/skeleton
и для традиционного сайта. $constraint = new Assert\Collection(
[
'fields' => [
'cf_sub_products' => [
new Assert\NotBlank(),
new Assert\Type('array'),
new Assert\All([
new Assert\Collection([
'fields' => [
"size" => [
new Assert\NotBlank(),
],
"cf_product_id" => [
new Assert\NotBlank(),
],
"enabled" => [
new Assert\NotBlank(),
],
"price" => [
new Assert\NotBlank(),
],
"sku" => [
new Assert\NotBlank(),
],
],
])
])
]
]
]
);
$violations = $validator->validate($body, $constraint);
'toEmail' => [
new Assert\NotBlank(),
new Assert\Type('array'),
new Assert\All([
'constraints' => [
new Assert\NotBlank(),
new Assert\Email(),
],
])
]
security.yml
security:
access_control:
- { path: ^/ }
RequestVoter.php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Security;
class RequestVoter implements VoterInterface
{
/**
* @var Security
*/
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
/**
* @inheritDoc
*/
public function vote(TokenInterface $token, $subject, array $attributes)
{
if (!$subject instanceof Request) {
return self::ACCESS_ABSTAIN;
}
$route = $subject->attributes->get('_route');
$routeRole = $this->getRoleByRoute($route);
if ($this->security->isGranted($routeRole)) {
return self::ACCESS_GRANTED;
}
return self::ACCESS_DENIED;
}
protected function getRoleByRoute(string $route): string
{
// ...
}
}
{% transchoice count with { '%count%': count } %}
%count% яблоко|%count% яблока|%count% яблок
{% endtranschoice %}
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class Model
{
/**
* @var string
*/
public $foo;
/**
* @var string
*/
public $bar;
/**
* @Assert\Callback()
*
* @param ExecutionContextInterface $context
* @param $payload
*/
public function validateFooBar(ExecutionContextInterface $context, $payload)
{
$fooViolations = $context->getValidator()->validate($this->foo, new Assert\NotBlank());
$barViolations = $context->getValidator()->validate($this->bar, new Assert\NotBlank());
if ($fooViolations->count() > 0 && $barViolations->count() > 0) {
$context->buildViolation('foo или bar должны быть заполнены')
->addViolation()
;
}
}
}