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
function json_tree_tags_recursive($data, &$result, $path = '')
{
foreach ($data as $key => $value) {
if ($path !== '') {
$result[$path . $key] = $value;
} elseif (!is_array($value)) {
$result[$key] = $value;
}
if (is_array($value)) {
json_tree_tags_recursive($value, $result, $path . $key . '_');
}
}
}
json_tree_tags_recursive($data, $result);
print_r($result);
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();
}
{
"require": {
"phpmailer/phpmailer": "^6.0",
"bogdaan/viber-bot-php": "^0.0.12",
"brickpop/php-rest-curl": "^1.0"
},
"repositories": {
"php-rest-curl": {
"type": "package",
"package": {
"name": "brickpop/php-rest-curl",
"version": "1.0.0",
"source": {
"url": "git@github.com:brickpop/php-rest-curl.git",
"type": "git",
"reference": "master"
},
"autoload": {
"files": ["rest.inc.php"]
}
}
}
}
}
use Symfony\Component\HttpFoundation\JsonResponse;
$data = [
'List' => [
0 => [
'id' => 'id0',
],
1 => [
'id' => 'id1',
],
],
];
dump(
new JsonResponse(
json_encode($data, JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_FORCE_OBJECT),
200,
[],
true
)
);
// or
$response = new JsonResponse($data);
$response->setEncodingOptions(JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_FORCE_OBJECT);
dump($response->getContent());
use Pagerfanta\Pagerfanta;
class PaginatedCollection
{
private int $page;
private int $size;
private int $total;
private int $pages;
private array $items;
public function __construct(Pagerfanta $pagerfanta)
{
$this->page = $pagerfanta->getCurrentPage();
$this->size = $pagerfanta->getMaxPerPage();
$this->total = $pagerfanta->getNbResults();
$this->pages = $pagerfanta->getNbPages();
$this->items = iterator_to_array($pagerfanta);
}
public function getPage(): int
{
return $this->page;
}
public function getSize(): int
{
return $this->size;
}
public function getTotal(): int
{
return $this->total;
}
public function getPages(): int
{
return $this->pages;
}
public function getItems(): array
{
return $this->items;
}
}
// Repository
public function findPaginatedBySupplierId(int $supplier_id)
{
return new Pagerfanta(DoctrineORMAdapter($this->getSupplierIdQueryBuilder($supplier_id)));
}
private function getSupplierIdQueryBuilder(int $supplier_id)
{
$qb = $this->createQueryBuilder('sj');
$qb = $qb
->select("sj")
->orderBy('sj.datetime', 'ASC')
->andWhere("sj.supplier = :supplier_id")
->setParameter("supplier_id", $supplier_id);
return $qb;
}
// Controller
public function action()
{
// ...
return $this->json($this->paginatedCollection($repository->findPaginatedBySupplierId($id), $request));
}
// AbstractController
protected function paginatedCollection(Pagerfanta $pagination, Request $request, int $size = 20): PaginatedCollection
{
$pagination = $this->paginate($pagination, $request, $size);
return new PaginatedCollection($pagination);
}
protected function paginate(Pagerfanta $pagination, Request $request, int $size = 20): Pagerfanta
{
$pagination->setMaxPerPage($size);
$pagination->setCurrentPage($request->query->getInt('page', 1));
return $pagination;
}
{
"page": 1,
"size": 20,
"total": 76,
"pages": 4,
"items": [...]
}
/**
* @var \App\Entity\IpContract[]
*
* @ORM\OneToMany(targetEntity="App\Entity\IpContract", mappedBy="users", orphanRemoval=true)
*/
private $ipContracts;
return $this->json($user, 200, [], [
AbstractNormalizer::IGNORED_ATTRIBUTES => ['__initializer__', '__cloner__', '__isInitialized__'],
]);
// App\Kernel
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader)
{
// ...
$container->addCompilerPass(new class implements CompilerPassInterface {
public function process(ContainerBuilder $container)
{
$container->getDefinition('serializer.normalizer.object')->setArgument(6, [
AbstractNormalizer::IGNORED_ATTRIBUTES => ['__initializer__', '__cloner__', '__isInitialized__'],
]);
}
});
}
abstract class AbstractAdapter extends \CloudCreativity\LaravelJsonApi\Eloquent\AbstractAdapter
{
public function __construct(CursorStrategy $paging)
{
parent::__construct($this->getModelInstance(), $paging);
}
abstract protected function getModelInstance();
// ...
}
// App\Kernel
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
{
// ...
$container->addCompilerPass(new class implements CompilerPassInterface {
public function process(ContainerBuilder $container)
{
$container->getDefinition('oneup_flysystem.adapter.local')->setLazy(true);
}
});
}
Since every developer uses a different IDE, the recommended way to enable this feature is to configure it on a system level. This can be done by setting the xdebug.file_link_format option in your php.ini configuration file.
# services.yaml
imports:
- { resource: 'local.yaml', ignore_errors: true }
# local.yaml
framework:
ide: 'phpstorm://open?file=%%f&line=%%l'
# .gitignore
/config/local.yaml