IS_AUTHENTICATED_FULLY
надо заменить на IS_AUTHENTICATED_REMEMBERED
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: ROLE_ADMIN }
- { path: ^/system/*, role: ROLE_ADMIN }
- { path: ^/*, role: IS_AUTHENTICATED_REMEMBERED }
interface ExecutorInterface
{
public function exec(): string;
}
class ConcreteImplementationA implements ExecutorInterface
{
public function exec(): string
{
return 'A';
}
}
class ConcreteImplementationB implements ExecutorInterface
{
public function exec(): string
{
return 'B';
}
}
class VariantExecutor
{
public function __construct(private ServiceLocator $locator)
{
}
public function exec(string $variant): string
{
return $this->locator->get($variant)->exec();
}
}
class Controller
{
public function call(VariantExecutor $executor, string $variant): Response
{
return new Response($executor->exec($variant));
}
}
#services.yaml
app.executor.locator:
class: Symfony\Component\DependencyInjection\ServiceLocator
arguments:
-
a: App\ConcreteImplementationA
b: App\ConcreteImplementationB
tags:
- { name: container.service_locator }
/**
* @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current
* env vars or be replaced by uniquely identifiable placeholders.
* Set to "true" when you want to use the current ContainerBuilder
* directly, keep to "false" when the container is dumped instead.
*/
public function compile(bool $resolveEnvPlaceholders = false)
$container->compile(true);
$ids = [1, 2, 3, 4];
$this->getEntityManager()->getConnection()
->executeStatement('UPDATE some_table SET some_field = ? WHERE id IN (?)', [
'value',
$ids,
], [
1 => Connection::PARAM_INT_ARRAY,
]);
$builder = $this->getEntityManager()->getConnection()->createQueryBuilder();
$builder
->update('some_table')
->set('some_field', ':value')
->where($builder->expr()->in('id', ':ids'))
->setParameter('value', 'value')
->setParameter('ids', $ids, Connection::PARAM_INT_ARRAY)
;
$builder->execute()
$this->getEntityManager()
->createQuery('UPDATE App\Entity\SomeEntity se SET se.someValue = :value WHERE se.id IN (:ids)')
->execute(new ArrayCollection([
new Parameter('value', 'value'),
new Parameter('ids', $ids),
]))
);
$builder = $this->createQueryBuilder('se');
$builder
->update()
->set('se.someValue', ':value')
->where($builder->expr()->in('uu.id', ':ids'))
->setParameter('value', 'value')
->setParameter('ids', $ids)
;
$builder->getQuery()->execute();
\App\Entity\Group::__toString
, либо добавьте опцию choice_label->add('group', null, [
'choice_label' => 'getName', // Метод, который вернет название группы
])
$post = new Post();
// Заполняем поля, которые присутствуют в форме
$post->setPublishedAt(new \DateTime());
$post->setTitle('New title');
// Если объекты не используются, то заполнять надо массив
// $post = [];
// $post['publishedAt'] = new \DateTime();
// $post['title'] = 'New title';
$form = $this->createForm(PostType::class, $post);
// ...
return $this->render('post.html.twig', [
'form' => $form->createView(),
])
$builder->get('publishedAt')->setData(new \DateTime());
$builder->get('title')->setData('New title');
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$data = $event->getData();
if ($data !== null) {
return;
}
$event->setData([
'title' => 'New title',
'publishedAt' => new \DateTime(),
]);
});
# servces.yaml
services:
Monolog\Handler\TelegramBotHandler:
arguments:
- '%env(TELEGRAM_BOT_KEY)%'
- '%env(TELEGRAM_CHANNEL)%'
# monolog.yaml
monolog:
handlers:
message:
type: fingers_crossed
action_level: error
excluded_http_codes: [ 400, 401, 403, 404 ]
buffer_size: 50
handler: deduplicated
deduplicated:
type: deduplication
handler: telegram
telegram: # Важна только эта часть
type: service
id: Monolog\Handler\TelegramBotHandler
level: debug
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
class BundleFormTypeExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [BundleFormType::class];
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->remove('someField');
}
}
namespace App\Doctor;
use App\Doctor\Check\CheckInterface;
final class Doctor
{
/**
* @var CheckInterface[]
*/
private iterable $checks;
public function __construct(iterable $checks)
{
$this->checks = $checks;
}
/**
* @return Violation[]|array
*/
public function check(): array
{
$violations = [];
foreach ($this->checks as $check) {
$violations[$check->feature()] = array_merge($violations[$check->feature()] ?? [], $check->violations());
}
return $violations;
}
}
namespace App\Doctor\Check;
use App\Doctor\Violation;
interface CheckInterface
{
public function feature(): string;
/**
* @return Violation[]
*/
public function violations(): array;
}
services:
_instanceof:
App\Doctor\Check\CheckInterface:
tags:
- { name: app.doctor.check }
App\Doctor\Doctor:
arguments:
$checks: !tagged app.doctor.check
public function doctorAction(): JsonResponse
{
return $this->json($this->doctor->check());
}
{
"foo": [], // Ok
"bar": [
{ "message": "Отсутствуют статусы", "treatment": "Добавьте статусы" }
]
}
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
class MyObjectDenormalizer implements DenormalizerInterface
{
private ObjectNormalizer $objectNormalizer;
private EntityManagerInterface $entityManager;
public function __construct(ObjectNormalizer $objectNormalizer, EntityManagerInterface $entityManager)
{
$this->objectNormalizer = $objectNormalizer;
$this->entityManager = $entityManager;
}
public function denormalize($data, string $type, string $format = null, array $context = [])
{
if ($id = $data['id'] ?? null) {
$object = $this->entityManager->getRepository($type)->find($id);
$context = [
AbstractObjectNormalizer::OBJECT_TO_POPULATE => $object,
];
unset($data['id']);
}
return $this->objectNormalizer->denormalize($data, $type, $format, $context);
}
public function supportsDenormalization($data, string $type, string $format = null)
{
return $this->objectNormalizer->supportsDenormalization($data, $type, $format);
}
}
/comment/{id}/edit
), т.к. есть возможность подменить id и отредактировать другую сущность (к которой, к примеру, у пользователя доступа нет) use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Event\LogoutEvent;
class LogoutRedirectSubscriber implements EventSubscriberInterface
{
private const KEY = 'logout';
private UrlGeneratorInterface $urlGgenerator;
public static function getSubscribedEvents()
{
return [
ExceptionEvent::class => ['onException', 2], // Before \Symfony\Component\Security\Http\Firewall\ExceptionListener
ResponseEvent::class => 'onResponse',
LogoutEvent::class => 'onLogout',
];
}
public function __construct(UrlGeneratorInterface $urlGgenerator)
{
$this->urlGgenerator = $urlGgenerator;
}
public function onException(ExceptionEvent $event): void
{
if (!$event->isMasterRequest()) {
return;
}
$exception = $event->getThrowable();
if (!$exception instanceof AccessDeniedException) {
return;
}
$session = $event->getRequest()->getSession();
if ($session->has(self::KEY)) {
$event->setResponse(new RedirectResponse($this->urlGgenerator->generate('index')));
$event->stopPropagation();
}
}
public function onResponse(ResponseEvent $event): void
{
if (!$event->isMasterRequest()) {
return;
}
if ($event->getResponse()->getStatusCode() >= 300) {
return;
}
$session = $event->getRequest()->getSession();
if ($session->has(self::KEY)) {
$session->remove(self::KEY);
}
}
public function onLogout(LogoutEvent $event): void
{
$event->getRequest()->getSession()->set(self::KEY, true);
}
}
options
и будете проверять их в обработчике LogoutEvent
Kernel.php
разбирать структуру папок и подключать все динамически там.migrations/
src/
-- Controller/
---- User/
---- ModuleName/
-- Entity/
---- User/
---- ModuleName/
-- User/
--- Dto/
--- Repository/
--- Service/
-- ModuleName/
--- Dto/
--- Repository/
--- Service/
// src/AppBundle/Form/CustomerType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
//....
$builder
->add('phones',CollectionType::class, array(
'by_reference' => false,
// ...
));
//....
}
class Customers implements UserInterface
{
/**
* @ORM\OneToMany(targetEntity="AppBundle\Entity\Phone", mappedBy="customer_id", cascade={"persist", "remove", "merge"})
*/
private $phones; // Это же коллекция, нужно множественное число, чтобы работали adder и remover
public function addPhone(Phone $phone)
{
$phone->setCustomer($this);
$this->phones->add($phone);
}
public function removePhone(Phone $phone)
{
$phone->setCustomer(null);
$this->phones->removeElement($phone);
}
}
// src/AppBundle/Form/PhoneType.php
// $builder->add('customerId', HiddenType::class); // не нужен
bin/console fos:user:create username em@ai.il 'p@55w0rd'
./**
* @psalm-suppress PropertyNotSetInConstructor
*/
class CreateCommand extends AbstractCommand
{
private DocumentManager $documentManager;
private ValidatorInterface $validator;
private PasswordGenerator $passwordGenerator;
private AdministratorMapper $mapper;
public function __construct(
DocumentManager $documentManager,
ValidatorInterface $validator,
PasswordGenerator $passwordGenerator,
AdministratorMapper $mapper
) {
parent::__construct();
$this->documentManager = $documentManager;
$this->validator = $validator;
$this->passwordGenerator = $passwordGenerator;
$this->mapper = $mapper;
}
/**
* @psalm-suppress MissingReturnType
*/
protected function configure()
{
$this
->setName('app:administrator:create')
->addArgument('username', InputArgument::REQUIRED, 'Username')
->addOption('password', 'p', InputOption::VALUE_REQUIRED, 'Password')
->setDescription('Creates administrator')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
/** @var string $username */
$username = $input->getArgument('username');
/** @var string|null $plainPassword */
$plainPassword = $input->getOption('password');
if (!$plainPassword) {
$plainPassword = $this->passwordGenerator->generate();
}
$model = new AdministratorModel();
$model->enabled = true;
$model->username = $username;
$model->password = $plainPassword;
$errors = $this->validator->validate($model);
if (\count($errors) > 0) {
$this->io->error('Can\'t create administrator');
$this->printConstraintViolations($errors);
return 1;
}
$administrator = $this->mapper->map($model);
$this->documentManager->persist($administrator);
$this->documentManager->flush();
$this->io->writeln(sprintf('Administrator <info>%s</info> with password <info>%s</info> has been created', $administrator->getUsername(), $plainPassword));
return 0;
}
}
bin/console app:administrator:create vasx3
maker:
root_namespace: 'App\Common'
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
AppСommon:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Common/Entity'
prefix: 'App\Common\Entity'
alias: AppСommon
specs
всегда один и тот же набор объектов и нет динамических параметров, то вложенные формы вполне могут сработать /** @var AuthorizationCheckerInterface $authorizationChecker */
if (!$authorizationChecker->isGranted('ROLE_USER')) {
throw new AccessDeniedException();
}
/** @var AuthorizationCheckerInterface $authorizationChecker */
if (!$authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
throw new AccessDeniedException();
}