Repository: Sylius/SyliusResourceBundle Branch: 1.15 Commit: 82583b128633 Files: 1079 Total size: 2.6 MB Directory structure: gitextract_w9ysgmua/ ├── .dockerignore ├── .gitattributes ├── .gitbook.yaml ├── .github/ │ ├── CODEOWNERS │ ├── CONTRIBUTING.md │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.md │ │ ├── documentation-issue.md │ │ ├── feature-request.md │ │ ├── security-issue.md │ │ └── support-question.md │ ├── ISSUE_TEMPLATE.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ └── workflows/ │ ├── build.yml │ ├── docker.yml │ ├── packages_split.yaml │ └── upmerge_pr.yaml ├── .gitignore ├── CHANGELOG.md ├── CONFLICTS.md ├── Dockerfile ├── LICENSE ├── LICENSE_OF_TRADEMARK_AND_LOGO ├── Makefile ├── README.md ├── UPGRADE.md ├── composer.json ├── docker-compose.yml ├── ecs.php ├── phpstan-baseline.neon ├── phpstan.neon ├── phpunit.xml.dist ├── psalm.xml ├── rector.php ├── src/ │ ├── Bundle/ │ │ ├── .gitignore │ │ ├── AbstractResourceBundle.php │ │ ├── Command/ │ │ │ └── DebugResourceCommand.php │ │ ├── Context/ │ │ │ ├── Initiator/ │ │ │ │ └── LegacyRequestContextInitiator.php │ │ │ └── Option/ │ │ │ └── RequestConfigurationOption.php │ │ ├── Controller/ │ │ │ ├── AuthorizationCheckerInterface.php │ │ │ ├── BcLayerRequestTrait.php │ │ │ ├── ContainerAwareTrait.php │ │ │ ├── ControllerTrait.php │ │ │ ├── DisabledAuthorizationChecker.php │ │ │ ├── EventDispatcher.php │ │ │ ├── EventDispatcherInterface.php │ │ │ ├── FlashHelper.php │ │ │ ├── FlashHelperInterface.php │ │ │ ├── NewResourceFactory.php │ │ │ ├── NewResourceFactoryInterface.php │ │ │ ├── Parameters.php │ │ │ ├── ParametersParser.php │ │ │ ├── ParametersParserInterface.php │ │ │ ├── RedirectHandler.php │ │ │ ├── RedirectHandlerInterface.php │ │ │ ├── RequestConfiguration.php │ │ │ ├── RequestConfigurationFactory.php │ │ │ ├── RequestConfigurationFactoryInterface.php │ │ │ ├── ResourceController.php │ │ │ ├── ResourceDeleteHandler.php │ │ │ ├── ResourceDeleteHandlerInterface.php │ │ │ ├── ResourceFormFactory.php │ │ │ ├── ResourceFormFactoryInterface.php │ │ │ ├── ResourceUpdateHandler.php │ │ │ ├── ResourceUpdateHandlerInterface.php │ │ │ ├── ResourcesCollectionProvider.php │ │ │ ├── ResourcesCollectionProviderInterface.php │ │ │ ├── ResourcesResolver.php │ │ │ ├── ResourcesResolverInterface.php │ │ │ ├── SingleResourceProvider.php │ │ │ ├── SingleResourceProviderInterface.php │ │ │ ├── StateMachine.php │ │ │ ├── StateMachineInterface.php │ │ │ ├── ViewHandler.php │ │ │ ├── ViewHandlerInterface.php │ │ │ └── Workflow.php │ │ ├── DependencyInjection/ │ │ │ ├── Compiler/ │ │ │ │ ├── CsrfTokenManagerPass.php │ │ │ │ ├── DoctrineContainerRepositoryFactoryPass.php │ │ │ │ ├── DoctrineTargetEntitiesResolverPass.php │ │ │ │ ├── Helper/ │ │ │ │ │ ├── TargetEntitiesResolver.php │ │ │ │ │ └── TargetEntitiesResolverInterface.php │ │ │ │ ├── PagerfantaBridgePass.php │ │ │ │ ├── PrioritizedCompositeServicePass.php │ │ │ │ ├── RegisterFormBuilderPass.php │ │ │ │ ├── RegisterFqcnControllersPass.php │ │ │ │ ├── RegisterResourceRepositoryPass.php │ │ │ │ ├── RegisterResourceStateMachinePass.php │ │ │ │ ├── RegisterResourcesPass.php │ │ │ │ ├── RegisterStateMachinePass.php │ │ │ │ ├── TwigPass.php │ │ │ │ ├── UnregisterFosRestDefinitionsPass.php │ │ │ │ ├── UnregisterHateoasDefinitionsPass.php │ │ │ │ └── WinzouStateMachinePass.php │ │ │ ├── Configuration.php │ │ │ ├── Driver/ │ │ │ │ ├── AbstractDriver.php │ │ │ │ ├── Doctrine/ │ │ │ │ │ ├── AbstractDoctrineDriver.php │ │ │ │ │ ├── DoctrineODMDriver.php │ │ │ │ │ ├── DoctrineORMDriver.php │ │ │ │ │ └── DoctrinePHPCRDriver.php │ │ │ │ ├── DriverInterface.php │ │ │ │ ├── DriverProvider.php │ │ │ │ └── Exception/ │ │ │ │ ├── InvalidDriverException.php │ │ │ │ └── UnknownDriverException.php │ │ │ ├── Extension/ │ │ │ │ └── AbstractResourceExtension.php │ │ │ ├── PagerfantaConfiguration.php │ │ │ ├── PagerfantaExtension.php │ │ │ └── SyliusResourceExtension.php │ │ ├── Doctrine/ │ │ │ ├── ODM/ │ │ │ │ ├── MongoDB/ │ │ │ │ │ ├── DocumentRepository.php │ │ │ │ │ └── TranslatableRepository.php │ │ │ │ └── PHPCR/ │ │ │ │ ├── DocumentRepository.php │ │ │ │ ├── EventListener/ │ │ │ │ │ ├── DefaultParentListener.php │ │ │ │ │ ├── NameFilterListener.php │ │ │ │ │ └── NameResolverListener.php │ │ │ │ └── Form/ │ │ │ │ └── Builder/ │ │ │ │ └── DefaultFormBuilder.php │ │ │ ├── ORM/ │ │ │ │ ├── ContainerRepositoryFactory.php │ │ │ │ ├── CreatePaginatorTrait.php │ │ │ │ ├── EntityRepository.php │ │ │ │ ├── Form/ │ │ │ │ │ └── Builder/ │ │ │ │ │ └── DefaultFormBuilder.php │ │ │ │ ├── ResourceLogEntryRepository.php │ │ │ │ ├── ResourceLogEntryRepositoryInterface.php │ │ │ │ └── ResourceRepositoryTrait.php │ │ │ └── ResourceMappingDriverChain.php │ │ ├── Event/ │ │ │ └── ResourceControllerEvent.php │ │ ├── EventListener/ │ │ │ ├── AbstractDoctrineListener.php │ │ │ ├── AbstractDoctrineSubscriber.php │ │ │ ├── ODMMappedSuperClassSubscriber.php │ │ │ ├── ODMRepositoryClassSubscriber.php │ │ │ ├── ODMTranslatableListener.php │ │ │ ├── ORMMappedSuperClassSubscriber.php │ │ │ ├── ORMRepositoryClassSubscriber.php │ │ │ └── ORMTranslatableListener.php │ │ ├── ExpressionLanguage/ │ │ │ ├── ExpressionLanguage.php │ │ │ └── NotNullExpressionFunctionProvider.php │ │ ├── Form/ │ │ │ ├── Builder/ │ │ │ │ └── DefaultFormBuilderInterface.php │ │ │ ├── DataTransformer/ │ │ │ │ ├── CollectionToStringTransformer.php │ │ │ │ ├── RecursiveTransformer.php │ │ │ │ └── ResourceToIdentifierTransformer.php │ │ │ ├── EventSubscriber/ │ │ │ │ └── AddCodeFormSubscriber.php │ │ │ ├── Extension/ │ │ │ │ ├── CollectionTypeExtension.php │ │ │ │ └── HttpFoundation/ │ │ │ │ └── HttpFoundationRequestHandler.php │ │ │ ├── Registry/ │ │ │ │ ├── FormTypeRegistry.php │ │ │ │ └── FormTypeRegistryInterface.php │ │ │ └── Type/ │ │ │ ├── AbstractResourceType.php │ │ │ ├── ArchivableType.php │ │ │ ├── DefaultResourceType.php │ │ │ ├── FixedCollectionType.php │ │ │ ├── ResourceAutocompleteChoiceType.php │ │ │ ├── ResourceToIdentifierType.php │ │ │ └── ResourceTranslationsType.php │ │ ├── Grid/ │ │ │ ├── Controller/ │ │ │ │ └── ResourcesResolver.php │ │ │ ├── Parser/ │ │ │ │ ├── OptionsParser.php │ │ │ │ └── OptionsParserInterface.php │ │ │ ├── Renderer/ │ │ │ │ ├── TwigBulkActionGridRenderer.php │ │ │ │ └── TwigGridRenderer.php │ │ │ └── View/ │ │ │ ├── LegacyGridViewFactory.php │ │ │ ├── ResourceGridView.php │ │ │ ├── ResourceGridViewFactory.php │ │ │ └── ResourceGridViewFactoryInterface.php │ │ ├── Provider/ │ │ │ └── RequestParameterProvider.php │ │ ├── ResourceBundleInterface.php │ │ ├── Resources/ │ │ │ ├── config/ │ │ │ │ ├── doctrine/ │ │ │ │ │ └── model/ │ │ │ │ │ └── AbstractTranslation.orm.xml │ │ │ │ ├── services/ │ │ │ │ │ ├── console.php │ │ │ │ │ ├── context.php │ │ │ │ │ ├── controller.php │ │ │ │ │ ├── dispatcher.php │ │ │ │ │ ├── expression_language.php │ │ │ │ │ ├── form.php │ │ │ │ │ ├── helper.php │ │ │ │ │ ├── integrations/ │ │ │ │ │ │ ├── doctrine/ │ │ │ │ │ │ │ ├── mongodb-odm.php │ │ │ │ │ │ │ ├── orm.php │ │ │ │ │ │ │ └── phpcr-odm.php │ │ │ │ │ │ ├── doctrine.php │ │ │ │ │ │ ├── grid.php │ │ │ │ │ │ └── translation.php │ │ │ │ │ ├── listener.php │ │ │ │ │ ├── metadata/ │ │ │ │ │ │ ├── extractor.php │ │ │ │ │ │ ├── inflector.php │ │ │ │ │ │ ├── mutator.php │ │ │ │ │ │ ├── path_segment_name_generator.php │ │ │ │ │ │ ├── repository_argument_resolver.php │ │ │ │ │ │ ├── resource_class_list.php │ │ │ │ │ │ ├── resource_metadata_collection.php │ │ │ │ │ │ └── resource_metadata_operation.php │ │ │ │ │ ├── metadata.php │ │ │ │ │ ├── routing/ │ │ │ │ │ │ ├── loader.php │ │ │ │ │ │ └── resource.php │ │ │ │ │ ├── routing.php │ │ │ │ │ ├── security.php │ │ │ │ │ ├── state/ │ │ │ │ │ │ ├── processor/ │ │ │ │ │ │ │ └── write.php │ │ │ │ │ │ ├── processor.php │ │ │ │ │ │ └── provider.php │ │ │ │ │ ├── state.php │ │ │ │ │ ├── state_machine.php │ │ │ │ │ ├── storage.php │ │ │ │ │ └── twig.php │ │ │ │ ├── services.php │ │ │ │ └── validation/ │ │ │ │ ├── AbstractTranslation.xml │ │ │ │ └── TranslatableInterface.xml │ │ │ ├── translations/ │ │ │ │ ├── flashes.ar.yml │ │ │ │ ├── flashes.be.yml │ │ │ │ ├── flashes.bg.yml │ │ │ │ ├── flashes.cs.yml │ │ │ │ ├── flashes.da.yml │ │ │ │ ├── flashes.de.yml │ │ │ │ ├── flashes.de_CH.yml │ │ │ │ ├── flashes.el.yml │ │ │ │ ├── flashes.en.yml │ │ │ │ ├── flashes.es.yml │ │ │ │ ├── flashes.fa.yml │ │ │ │ ├── flashes.fr.yml │ │ │ │ ├── flashes.hr.yml │ │ │ │ ├── flashes.hu.yml │ │ │ │ ├── flashes.id.yml │ │ │ │ ├── flashes.it.yml │ │ │ │ ├── flashes.ja.yml │ │ │ │ ├── flashes.lt.yml │ │ │ │ ├── flashes.nl.yml │ │ │ │ ├── flashes.no.yml │ │ │ │ ├── flashes.pl.yml │ │ │ │ ├── flashes.pt.yml │ │ │ │ ├── flashes.pt_BR.yml │ │ │ │ ├── flashes.ro.yml │ │ │ │ ├── flashes.ru.yml │ │ │ │ ├── flashes.sk.yml │ │ │ │ ├── flashes.sl.yml │ │ │ │ ├── flashes.sq.yml │ │ │ │ ├── flashes.sr.yml │ │ │ │ ├── flashes.sr_CS.yml │ │ │ │ ├── flashes.th.yml │ │ │ │ ├── flashes.tr.yml │ │ │ │ ├── flashes.uk.yml │ │ │ │ ├── flashes.zh_CN.yml │ │ │ │ ├── messages.ar.yml │ │ │ │ ├── messages.be.yml │ │ │ │ ├── messages.bg.yml │ │ │ │ ├── messages.ca.yml │ │ │ │ ├── messages.cs.yml │ │ │ │ ├── messages.da.yml │ │ │ │ ├── messages.de.yml │ │ │ │ ├── messages.de_CH.yml │ │ │ │ ├── messages.el.yml │ │ │ │ ├── messages.en.yml │ │ │ │ ├── messages.es.yml │ │ │ │ ├── messages.fa.yml │ │ │ │ ├── messages.fi.yml │ │ │ │ ├── messages.fr.yml │ │ │ │ ├── messages.he.yml │ │ │ │ ├── messages.hr.yml │ │ │ │ ├── messages.hu.yml │ │ │ │ ├── messages.id.yml │ │ │ │ ├── messages.it.yml │ │ │ │ ├── messages.ja.yml │ │ │ │ ├── messages.lt.yml │ │ │ │ ├── messages.nl.yml │ │ │ │ ├── messages.no.yml │ │ │ │ ├── messages.pl.yml │ │ │ │ ├── messages.pt.yml │ │ │ │ ├── messages.pt_BR.yml │ │ │ │ ├── messages.ro.yml │ │ │ │ ├── messages.ru.yml │ │ │ │ ├── messages.sk.yml │ │ │ │ ├── messages.sl.yml │ │ │ │ ├── messages.sq.yml │ │ │ │ ├── messages.sr.yml │ │ │ │ ├── messages.sr_CS.yml │ │ │ │ ├── messages.sv.yml │ │ │ │ ├── messages.th.yml │ │ │ │ ├── messages.tr.yml │ │ │ │ ├── messages.uk.yml │ │ │ │ ├── messages.zh_CN.yml │ │ │ │ ├── messages.zh_TW.yml │ │ │ │ ├── validators.ar.yml │ │ │ │ ├── validators.bg.yml │ │ │ │ ├── validators.cs.yml │ │ │ │ ├── validators.da.yml │ │ │ │ ├── validators.de.yml │ │ │ │ ├── validators.de_CH.yml │ │ │ │ ├── validators.el.yml │ │ │ │ ├── validators.en.yml │ │ │ │ ├── validators.es.yml │ │ │ │ ├── validators.fa.yml │ │ │ │ ├── validators.fr.yml │ │ │ │ ├── validators.he.yml │ │ │ │ ├── validators.hr.yml │ │ │ │ ├── validators.hu.yml │ │ │ │ ├── validators.id.yml │ │ │ │ ├── validators.it.yml │ │ │ │ ├── validators.lt.yml │ │ │ │ ├── validators.nl.yml │ │ │ │ ├── validators.pl.yml │ │ │ │ ├── validators.pt.yml │ │ │ │ ├── validators.pt_BR.yml │ │ │ │ ├── validators.ro.yml │ │ │ │ ├── validators.ru.yml │ │ │ │ ├── validators.sk.yml │ │ │ │ ├── validators.sl.yml │ │ │ │ ├── validators.sq.yml │ │ │ │ ├── validators.sr_CS.yml │ │ │ │ ├── validators.th.yml │ │ │ │ ├── validators.tr.yml │ │ │ │ ├── validators.uk.yml │ │ │ │ └── validators.zh_CN.yml │ │ │ └── views/ │ │ │ ├── Macros/ │ │ │ │ ├── actions.html.twig │ │ │ │ ├── buttons.html.twig │ │ │ │ └── notification.html.twig │ │ │ ├── Twig/ │ │ │ │ ├── paginate.html.twig │ │ │ │ └── sorting.html.twig │ │ │ └── forms.html.twig │ │ ├── Routing/ │ │ │ ├── Configuration.php │ │ │ ├── CrudRoutesAttributesLoader.php │ │ │ ├── ResourceLoader.php │ │ │ ├── RouteAttributesFactory.php │ │ │ ├── RouteAttributesFactoryInterface.php │ │ │ ├── RouteFactory.php │ │ │ ├── RouteFactoryInterface.php │ │ │ └── RoutesAttributesLoader.php │ │ ├── Storage/ │ │ │ ├── CookieStorage.php │ │ │ └── SessionStorage.php │ │ ├── SyliusResourceBundle.php │ │ ├── Twig/ │ │ │ └── Context/ │ │ │ └── LegacyContextFactory.php │ │ └── Validator/ │ │ ├── Constraints/ │ │ │ ├── Disabled.php │ │ │ ├── Enabled.php │ │ │ └── UniqueWithinCollectionConstraint.php │ │ ├── DisabledValidator.php │ │ ├── EnabledValidator.php │ │ └── UniqueWithinCollectionConstraintValidator.php │ └── Component/ │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ ├── legacy/ │ │ ├── phpspec.yml.dist │ │ ├── src/ │ │ │ ├── Annotation/ │ │ │ │ ├── SyliusCrudRoutes.php │ │ │ │ └── SyliusRoute.php │ │ │ ├── Exception/ │ │ │ │ ├── DeleteHandlingException.php │ │ │ │ ├── RaceConditionException.php │ │ │ │ ├── UnexpectedTypeException.php │ │ │ │ ├── UnsupportedMethodException.php │ │ │ │ ├── UpdateHandlingException.php │ │ │ │ └── VariantWithNoOptionsValuesException.php │ │ │ ├── Factory/ │ │ │ │ ├── Factory.php │ │ │ │ ├── FactoryInterface.php │ │ │ │ ├── TranslatableFactory.php │ │ │ │ └── TranslatableFactoryInterface.php │ │ │ ├── Generator/ │ │ │ │ ├── RandomnessGenerator.php │ │ │ │ └── RandomnessGeneratorInterface.php │ │ │ ├── Metadata/ │ │ │ │ ├── Metadata.php │ │ │ │ ├── MetadataInterface.php │ │ │ │ ├── Registry.php │ │ │ │ └── RegistryInterface.php │ │ │ ├── Model/ │ │ │ │ ├── AbstractTranslation.php │ │ │ │ ├── ArchivableInterface.php │ │ │ │ ├── ArchivableTrait.php │ │ │ │ ├── CodeAwareInterface.php │ │ │ │ ├── ResourceInterface.php │ │ │ │ ├── ResourceLogEntry.php │ │ │ │ ├── SlugAwareInterface.php │ │ │ │ ├── TimestampableInterface.php │ │ │ │ ├── TimestampableTrait.php │ │ │ │ ├── ToggleableInterface.php │ │ │ │ ├── ToggleableTrait.php │ │ │ │ ├── TranslatableInterface.php │ │ │ │ ├── TranslatableTrait.php │ │ │ │ ├── TranslationInterface.php │ │ │ │ └── VersionedInterface.php │ │ │ ├── Reflection/ │ │ │ │ └── ClassReflection.php │ │ │ ├── Repository/ │ │ │ │ ├── Exception/ │ │ │ │ │ └── ExistingResourceException.php │ │ │ │ ├── InMemoryRepository.php │ │ │ │ └── RepositoryInterface.php │ │ │ ├── ResourceActions.php │ │ │ ├── StateMachine/ │ │ │ │ ├── StateMachine.php │ │ │ │ └── StateMachineInterface.php │ │ │ ├── Storage/ │ │ │ │ └── StorageInterface.php │ │ │ └── Translation/ │ │ │ ├── Provider/ │ │ │ │ ├── ImmutableTranslationLocaleProvider.php │ │ │ │ └── TranslationLocaleProviderInterface.php │ │ │ ├── TranslatableEntityLocaleAssigner.php │ │ │ └── TranslatableEntityLocaleAssignerInterface.php │ │ └── tests/ │ │ ├── Dummy/ │ │ │ ├── DummyClassOne.php │ │ │ ├── DummyClassTwo.php │ │ │ ├── DummyMultiResourcesWithOperations.php │ │ │ ├── DummyOperationsWithoutResource.php │ │ │ ├── DummyResource.php │ │ │ ├── DummyResourceWithAlias.php │ │ │ ├── DummyResourceWithDenormalizationContext.php │ │ │ ├── DummyResourceWithFormOptions.php │ │ │ ├── DummyResourceWithFormType.php │ │ │ ├── DummyResourceWithGrid.php │ │ │ ├── DummyResourceWithName.php │ │ │ ├── DummyResourceWithNormalizationContext.php │ │ │ ├── DummyResourceWithOperations.php │ │ │ ├── DummyResourceWithPluralName.php │ │ │ ├── DummyResourceWithRouteCondition.php │ │ │ ├── DummyResourceWithRoutePrefix.php │ │ │ ├── DummyResourceWithRoutePriorities.php │ │ │ ├── DummyResourceWithRouteRequirements.php │ │ │ ├── DummyResourceWithSections.php │ │ │ ├── DummyResourceWithSectionsAndNestedOperations.php │ │ │ ├── DummyResourceWithValidationContext.php │ │ │ ├── ProcessorWithCallable.php │ │ │ ├── ProviderWithCallable.php │ │ │ ├── RepositoryWithCallables.php │ │ │ ├── ResponderWithCallable.php │ │ │ └── TraitPass.php │ │ ├── Exception/ │ │ │ ├── DeleteHandlingExceptionTest.php │ │ │ ├── RaceConditionExceptionTest.php │ │ │ ├── UnexpectedTypeExceptionTest.php │ │ │ ├── UnsupportedMethodExceptionTest.php │ │ │ └── UpdateHandlingExceptionTest.php │ │ ├── Factory/ │ │ │ ├── FactoryTest.php │ │ │ └── TranslatableFactoryTest.php │ │ ├── Fixtures/ │ │ │ └── SampleBookResourceInterface.php │ │ ├── Metadata/ │ │ │ ├── MetadataTest.php │ │ │ └── RegistryTest.php │ │ ├── Model/ │ │ │ └── AbstractTranslationTest.php │ │ ├── Reflection/ │ │ │ ├── ClassInfoTraitTest.php │ │ │ └── ClassReflectionTest.php │ │ ├── Repository/ │ │ │ ├── Exception/ │ │ │ │ └── ExistingResourceExceptionTest.php │ │ │ └── InMemoryRepositoryTest.php │ │ ├── ResourceActionsTest.php │ │ ├── StateMachine/ │ │ │ └── StateMachineTest.php │ │ ├── Symfony/ │ │ │ ├── EventListener/ │ │ │ │ └── AddFormatListenerTest.php │ │ │ └── Validator/ │ │ │ └── State/ │ │ │ └── ValidateProviderTest.php │ │ └── Translation/ │ │ ├── Provider/ │ │ │ └── ImmutableTranslationLocaleProviderTest.php │ │ └── TranslatableEntityLocaleAssignerTest.php │ ├── phpunit.xml.dist │ ├── src/ │ │ ├── Annotation/ │ │ │ ├── SyliusCrudRoutes.php │ │ │ └── SyliusRoute.php │ │ ├── Context/ │ │ │ ├── Context.php │ │ │ ├── Initiator/ │ │ │ │ ├── RequestContextInitiator.php │ │ │ │ └── RequestContextInitiatorInterface.php │ │ │ └── Option/ │ │ │ ├── MetadataOption.php │ │ │ ├── RequestOption.php │ │ │ └── ResourceClassOption.php │ │ ├── Doctrine/ │ │ │ ├── Common/ │ │ │ │ ├── Metadata/ │ │ │ │ │ └── Resource/ │ │ │ │ │ └── Factory/ │ │ │ │ │ └── DoctrineResourceMetadataCollectionFactory.php │ │ │ │ └── State/ │ │ │ │ ├── PersistProcessor.php │ │ │ │ └── RemoveProcessor.php │ │ │ └── Persistence/ │ │ │ ├── Exception/ │ │ │ │ ├── ExceptionInterface.php │ │ │ │ └── ResourceExistsException.php │ │ │ ├── InMemoryRepository.php │ │ │ └── RepositoryInterface.php │ │ ├── Exception/ │ │ │ ├── DeleteHandlingException.php │ │ │ ├── DeleteResourceException.php │ │ │ ├── Exception.php │ │ │ ├── ExceptionInterface.php │ │ │ ├── InvalidArgumentException.php │ │ │ ├── LogicException.php │ │ │ ├── RaceConditionException.php │ │ │ ├── RuntimeException.php │ │ │ ├── StorageUnavailableException.php │ │ │ ├── UnexpectedTypeException.php │ │ │ ├── UnsupportedMethodException.php │ │ │ ├── UpdateHandlingException.php │ │ │ ├── VariantWithNoOptionsValuesException.php │ │ │ └── WriteResourceException.php │ │ ├── Factory/ │ │ │ ├── Factory.php │ │ │ ├── FactoryInterface.php │ │ │ ├── TranslatableFactory.php │ │ │ └── TranslatableFactoryInterface.php │ │ ├── Generator/ │ │ │ ├── RandomnessGenerator.php │ │ │ └── RandomnessGeneratorInterface.php │ │ ├── Grid/ │ │ │ ├── State/ │ │ │ │ └── RequestGridProvider.php │ │ │ └── View/ │ │ │ └── Factory/ │ │ │ ├── GridViewFactory.php │ │ │ └── GridViewFactoryInterface.php │ │ ├── Humanizer/ │ │ │ └── StringHumanizer.php │ │ ├── Metadata/ │ │ │ ├── Api/ │ │ │ │ ├── ApiOperationInterface.php │ │ │ │ ├── Delete.php │ │ │ │ ├── Get.php │ │ │ │ ├── GetCollection.php │ │ │ │ ├── Patch.php │ │ │ │ ├── Post.php │ │ │ │ └── Put.php │ │ │ ├── ApplyStateMachineTransition.php │ │ │ ├── AsOperationMutator.php │ │ │ ├── AsResource.php │ │ │ ├── AsResourceMutator.php │ │ │ ├── BulkDelete.php │ │ │ ├── BulkOperationInterface.php │ │ │ ├── BulkUpdate.php │ │ │ ├── CollectionOperationInterface.php │ │ │ ├── Create.php │ │ │ ├── CreateOperationInterface.php │ │ │ ├── Delete.php │ │ │ ├── DeleteOperationInterface.php │ │ │ ├── Extractor/ │ │ │ │ ├── AbstractResourceExtractor.php │ │ │ │ ├── PhpFileResourceExtractor.php │ │ │ │ └── ResourceExtractorInterface.php │ │ │ ├── FactoryAwareOperationInterface.php │ │ │ ├── GridAwareOperationInterface.php │ │ │ ├── HttpOperation.php │ │ │ ├── Index.php │ │ │ ├── Inflector/ │ │ │ │ ├── Inflector.php │ │ │ │ └── InflectorInterface.php │ │ │ ├── Metadata.php │ │ │ ├── MetadataInterface.php │ │ │ ├── Mutator/ │ │ │ │ ├── OperationMutatorCollection.php │ │ │ │ ├── OperationMutatorCollectionInterface.php │ │ │ │ ├── ResourceMutatorCollection.php │ │ │ │ └── ResourceMutatorCollectionInterface.php │ │ │ ├── Operation/ │ │ │ │ ├── DashPathSegmentNameGenerator.php │ │ │ │ ├── HttpOperationInitiator.php │ │ │ │ ├── HttpOperationInitiatorInterface.php │ │ │ │ ├── PathSegmentNameGeneratorInterface.php │ │ │ │ └── UnderscorePathSegmentNameGenerator.php │ │ │ ├── Operation.php │ │ │ ├── OperationAccessCheckerInterface.php │ │ │ ├── OperationMutatorInterface.php │ │ │ ├── Operations.php │ │ │ ├── Registry.php │ │ │ ├── RegistryInterface.php │ │ │ ├── Resource/ │ │ │ │ ├── Factory/ │ │ │ │ │ ├── AttributesResourceClassListFactory.php │ │ │ │ │ ├── AttributesResourceMetadataCollectionFactory.php │ │ │ │ │ ├── CachedResourceClassListFactory.php │ │ │ │ │ ├── CachedResourceMetadataCollectionFactory.php │ │ │ │ │ ├── EventShortNameResourceMetadataCollectionFactory.php │ │ │ │ │ ├── FactoryResourceMetadataCollectionFactory.php │ │ │ │ │ ├── MutatorResourceMetadataCollectionFactory.php │ │ │ │ │ ├── OperationDefaultsTrait.php │ │ │ │ │ ├── PhpFileResourceClassListFactory.php │ │ │ │ │ ├── PhpFileResourceMetadataCollectionFactory.php │ │ │ │ │ ├── PluralNameResourceMetadataCollectionFactory.php │ │ │ │ │ ├── ProviderResourceMetadataCollectionFactory.php │ │ │ │ │ ├── RedirectResourceMetadataCollectionFactory.php │ │ │ │ │ ├── ResourceClassListFactoryInterface.php │ │ │ │ │ ├── ResourceMetadataCollectionFactoryInterface.php │ │ │ │ │ ├── StateMachineResourceMetadataCollectionFactory.php │ │ │ │ │ ├── TemplatesDirResourceMetadataCollectionFactory.php │ │ │ │ │ └── VarsResourceMetadataCollectionFactory.php │ │ │ │ ├── ResourceClassList.php │ │ │ │ └── ResourceMetadataCollection.php │ │ │ ├── ResourceMetadata.php │ │ │ ├── ResourceMutatorInterface.php │ │ │ ├── Show.php │ │ │ ├── ShowOperationInterface.php │ │ │ ├── StateMachineAwareOperationInterface.php │ │ │ ├── Update.php │ │ │ ├── UpdateOperationInterface.php │ │ │ └── Util/ │ │ │ └── CachedTrait.php │ │ ├── Model/ │ │ │ ├── AbstractTranslation.php │ │ │ ├── ArchivableInterface.php │ │ │ ├── ArchivableTrait.php │ │ │ ├── CodeAwareInterface.php │ │ │ ├── ResourceInterface.php │ │ │ ├── ResourceLogEntry.php │ │ │ ├── SlugAwareInterface.php │ │ │ ├── TimestampableInterface.php │ │ │ ├── TimestampableTrait.php │ │ │ ├── ToggleableInterface.php │ │ │ ├── ToggleableTrait.php │ │ │ ├── TranslatableInterface.php │ │ │ ├── TranslatableTrait.php │ │ │ ├── TranslationInterface.php │ │ │ └── VersionedInterface.php │ │ ├── Reflection/ │ │ │ ├── CallableReflection.php │ │ │ ├── ClassInfoTrait.php │ │ │ ├── ClassReflection.php │ │ │ ├── Filter/ │ │ │ │ └── FunctionArgumentsFilter.php │ │ │ └── ReflectionClassRecursiveIterator.php │ │ ├── ResourceActions.php │ │ ├── State/ │ │ │ ├── Factory.php │ │ │ ├── FactoryInterface.php │ │ │ ├── Processor/ │ │ │ │ ├── BulkAwareProcessor.php │ │ │ │ ├── EventDispatcherBulkAwareProcessor.php │ │ │ │ ├── FlashProcessor.php │ │ │ │ ├── RespondProcessor.php │ │ │ │ └── WriteProcessor.php │ │ │ ├── Processor.php │ │ │ ├── ProcessorInterface.php │ │ │ ├── Provider/ │ │ │ │ ├── FactoryProvider.php │ │ │ │ ├── ReadProvider.php │ │ │ │ └── SecurityProvider.php │ │ │ ├── Provider.php │ │ │ ├── ProviderInterface.php │ │ │ ├── Responder.php │ │ │ └── ResponderInterface.php │ │ ├── StateMachine/ │ │ │ ├── OperationStateMachine.php │ │ │ ├── OperationStateMachineInterface.php │ │ │ ├── State/ │ │ │ │ └── ApplyStateMachineTransitionProcessor.php │ │ │ ├── StateMachine.php │ │ │ └── StateMachineInterface.php │ │ ├── Storage/ │ │ │ └── StorageInterface.php │ │ ├── Symfony/ │ │ │ ├── Controller/ │ │ │ │ └── MainController.php │ │ │ ├── DependencyInjection/ │ │ │ │ └── Compiler/ │ │ │ │ ├── DisableMetadataCachePass.php │ │ │ │ ├── FallbackToKernelDefaultLocalePass.php │ │ │ │ └── MetadataMutatorPass.php │ │ │ ├── EventDispatcher/ │ │ │ │ ├── GenericEvent.php │ │ │ │ ├── OperationEvent.php │ │ │ │ ├── OperationEventDispatcher.php │ │ │ │ ├── OperationEventDispatcherInterface.php │ │ │ │ ├── OperationEventHandler.php │ │ │ │ ├── OperationEventHandlerInterface.php │ │ │ │ └── State/ │ │ │ │ ├── DispatchPostReadEventProvider.php │ │ │ │ ├── DispatchPostWriteEventProcessor.php │ │ │ │ └── DispatchPreWriteEventProcessor.php │ │ │ ├── EventListener/ │ │ │ │ └── AddFormatListener.php │ │ │ ├── ExpressionLanguage/ │ │ │ │ ├── ArgumentParser.php │ │ │ │ ├── ArgumentParserInterface.php │ │ │ │ ├── Provider/ │ │ │ │ │ └── ThrowNotFoundOnNullExpressionFunctionProvider.php │ │ │ │ ├── RequestVariables.php │ │ │ │ ├── SyliusRepositoriesVariables.php │ │ │ │ ├── TokenVariables.php │ │ │ │ ├── VariablesCollection.php │ │ │ │ ├── VariablesCollectionInterface.php │ │ │ │ ├── VariablesInterface.php │ │ │ │ ├── VarsResolver.php │ │ │ │ └── VarsResolverInterface.php │ │ │ ├── Form/ │ │ │ │ ├── Factory/ │ │ │ │ │ ├── FormFactory.php │ │ │ │ │ └── FormFactoryInterface.php │ │ │ │ └── State/ │ │ │ │ └── FormProvider.php │ │ │ ├── Request/ │ │ │ │ ├── RepositoryArgumentResolver.php │ │ │ │ └── State/ │ │ │ │ ├── ApiResponder.php │ │ │ │ ├── Provider.php │ │ │ │ ├── Responder.php │ │ │ │ └── TwigResponder.php │ │ │ ├── Response/ │ │ │ │ ├── ApiHeadersInitiator.php │ │ │ │ └── HeadersInitiatorInterface.php │ │ │ ├── Routing/ │ │ │ │ ├── Factory/ │ │ │ │ │ ├── AttributesOperationRouteFactory.php │ │ │ │ │ ├── AttributesOperationRouteFactoryInterface.php │ │ │ │ │ ├── OperationRouteFactory.php │ │ │ │ │ ├── OperationRouteFactoryInterface.php │ │ │ │ │ ├── Resource/ │ │ │ │ │ │ ├── ResourceRouteCollectionFactory.php │ │ │ │ │ │ └── ResourceRouteCollectionFactoryInterface.php │ │ │ │ │ ├── RouteName/ │ │ │ │ │ │ ├── OperationRouteNameFactory.php │ │ │ │ │ │ └── OperationRouteNameFactoryInterface.php │ │ │ │ │ └── RoutePath/ │ │ │ │ │ ├── BulkOperationRoutePathFactory.php │ │ │ │ │ ├── CollectionOperationRoutePathFactory.php │ │ │ │ │ ├── CreateOperationRoutePathFactory.php │ │ │ │ │ ├── DeleteOperationRoutePathFactory.php │ │ │ │ │ ├── OperationRoutePathFactory.php │ │ │ │ │ ├── OperationRoutePathFactoryInterface.php │ │ │ │ │ ├── ShowOperationRoutePathFactory.php │ │ │ │ │ └── UpdateOperationRoutePathFactory.php │ │ │ │ ├── Loader/ │ │ │ │ │ └── ResourceLoader.php │ │ │ │ ├── RedirectHandler.php │ │ │ │ └── RedirectHandlerInterface.php │ │ │ ├── Security/ │ │ │ │ └── OperationAccessChecker.php │ │ │ ├── Serializer/ │ │ │ │ └── State/ │ │ │ │ ├── DeserializeProvider.php │ │ │ │ └── SerializeProcessor.php │ │ │ ├── Session/ │ │ │ │ └── Flash/ │ │ │ │ ├── FlashHelper.php │ │ │ │ └── FlashHelperInterface.php │ │ │ ├── Validator/ │ │ │ │ ├── EventListener/ │ │ │ │ │ └── ValidationExceptionListener.php │ │ │ │ ├── Exception/ │ │ │ │ │ ├── ConstraintViolationListAwareExceptionInterface.php │ │ │ │ │ └── ValidationException.php │ │ │ │ └── State/ │ │ │ │ └── ValidateProvider.php │ │ │ └── Workflow/ │ │ │ └── OperationStateMachine.php │ │ ├── Translation/ │ │ │ ├── Provider/ │ │ │ │ ├── ImmutableTranslationLocaleProvider.php │ │ │ │ └── TranslationLocaleProviderInterface.php │ │ │ ├── TranslatableEntityLocaleAssigner.php │ │ │ └── TranslatableEntityLocaleAssignerInterface.php │ │ ├── Twig/ │ │ │ └── Context/ │ │ │ └── Factory/ │ │ │ ├── ContextFactory.php │ │ │ ├── ContextFactoryInterface.php │ │ │ ├── DefaultContextFactory.php │ │ │ └── RequestContextFactory.php │ │ └── Winzou/ │ │ └── StateMachine/ │ │ └── OperationStateMachine.php │ └── tests/ │ ├── Context/ │ │ ├── ContextTest.php │ │ ├── Initiator/ │ │ │ └── RequestContextInitiatorTest.php │ │ └── Option/ │ │ ├── MetadataOptionTest.php │ │ └── RequestOptionTest.php │ ├── Doctrine/ │ │ ├── Common/ │ │ │ ├── Metadata/ │ │ │ │ └── Resource/ │ │ │ │ └── Factory/ │ │ │ │ └── DoctrineResourceMetadataCollectionFactoryTest.php │ │ │ └── State/ │ │ │ ├── PersistProcessorTest.php │ │ │ └── RemoveProcessorTest.php │ │ └── Persistence/ │ │ ├── Exception/ │ │ │ └── ResourceExistsExceptionTest.php │ │ └── InMemoryRepositoryTest.php │ ├── Dummy/ │ │ ├── DummyResource.php │ │ └── PullRequest.php │ ├── Exception/ │ │ └── RaceConditionExceptionTest.php │ ├── Factory/ │ │ ├── FactoryTest.php │ │ └── TranslatableFactoryTest.php │ ├── Generator/ │ │ └── RandomnessGeneratorTest.php │ ├── Grid/ │ │ └── State/ │ │ └── RequestGridProviderTest.php │ ├── Humanizer/ │ │ └── StringHumanizerTest.php │ ├── Metadata/ │ │ ├── ApplyStateMachineTransitionTest.php │ │ ├── BulkDeleteTest.php │ │ ├── CreateTest.php │ │ ├── DeleteTest.php │ │ ├── Extractor/ │ │ │ ├── PhpFileResourceExtractorTest.php │ │ │ └── php/ │ │ │ ├── another_valid_php_file.php │ │ │ ├── invalid_php_file.php │ │ │ └── valid_php_file.php │ │ ├── HttpOperationTest.php │ │ ├── IndexTest.php │ │ ├── Inflector/ │ │ │ └── InflectorTest.php │ │ ├── MetadataTest.php │ │ ├── Operation/ │ │ │ ├── DashPathSegmentNameGeneratorTest.php │ │ │ ├── HttpOperationInitiatorTest.php │ │ │ └── UnderscorePathSegmentNameGeneratorTest.php │ │ ├── OperationsTest.php │ │ ├── RegistryTest.php │ │ ├── Resource/ │ │ │ ├── Factory/ │ │ │ │ ├── AttributesResourceClassListFactoryTest.php │ │ │ │ ├── AttributesResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── CachedResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── EventShortNameResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── FactoryResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── MutatorResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── PhpFileResourceClassListFactoryTest.php │ │ │ │ ├── PhpFileResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── PluralNameResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── ProviderResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── RedirectResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── StateMachineResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── TemplatesDirResourceMetadataCollectionFactoryTest.php │ │ │ │ ├── VarsResourceMetadataCollectionFactoryTest.php │ │ │ │ └── php/ │ │ │ │ ├── php_file_with_resource_class.php │ │ │ │ └── php_file_without_resource_class.php │ │ │ ├── ResourceClassListTest.php │ │ │ └── ResourceMetadataCollectionTest.php │ │ ├── ResourceMetadataTest.php │ │ ├── ShowTest.php │ │ └── UpdateTest.php │ ├── Model/ │ │ └── AbstractTranslationTest.php │ ├── Reflection/ │ │ ├── CallableReflectionTest.php │ │ ├── Filter/ │ │ │ └── FunctionArgumentsFilterTest.php │ │ └── ReflectionClassRecursiveIteratorTest.php │ ├── State/ │ │ ├── FactoryTest.php │ │ ├── Processor/ │ │ │ ├── BulkAwareProcessorTest.php │ │ │ ├── EventDispatcherBulkAwareProcessorTest.php │ │ │ ├── FlashProcessorTest.php │ │ │ ├── RespondProcessorTest.php │ │ │ └── WriteProcessorTest.php │ │ ├── ProcessorTest.php │ │ ├── Provider/ │ │ │ ├── FactoryProviderTest.php │ │ │ └── ReadProviderTest.php │ │ ├── ProviderTest.php │ │ └── ResponderTest.php │ ├── StateMachine/ │ │ ├── OperationStateMachineTest.php │ │ ├── State/ │ │ │ └── ApplyStateMachineTransitionProcessorTest.php │ │ └── StateMachineTest.php │ ├── Symfony/ │ │ ├── Controller/ │ │ │ └── MainControllerTest.php │ │ ├── DependencyInjection/ │ │ │ └── Compiler/ │ │ │ ├── DisableMetadataCachePassTest.php │ │ │ ├── FallbackToKernelDefaultLocalePassTest.php │ │ │ └── MetadataMutatorPassTest.php │ │ ├── EventDispatcher/ │ │ │ ├── GenericEventTest.php │ │ │ ├── OperationEventDispatcherTest.php │ │ │ ├── OperationEventHandlerTest.php │ │ │ ├── OperationEventTest.php │ │ │ └── State/ │ │ │ ├── DispatchPostReadEventProviderTest.php │ │ │ ├── DispatchPostWriteEventProcessorTest.php │ │ │ └── DispatchPreWriteEventProcessorTest.php │ │ ├── EventListener/ │ │ │ └── AddFormatListenerTest.php │ │ ├── ExpressionLanguage/ │ │ │ ├── ArgumentParserTest.php │ │ │ ├── Provider/ │ │ │ │ └── ThrowNotFoundOnNullExpressionFunctionProviderTest.php │ │ │ ├── RequestVariablesTest.php │ │ │ ├── SyliusRepositoriesVariablesTest.php │ │ │ ├── TokenVariablesTest.php │ │ │ ├── VariablesCollectionTest.php │ │ │ └── VarsResolverTest.php │ │ ├── Form/ │ │ │ ├── Factory/ │ │ │ │ └── FormFactoryTest.php │ │ │ └── State/ │ │ │ └── FormProviderTest.php │ │ ├── Request/ │ │ │ ├── RepositoryArgumentResolverTest.php │ │ │ └── State/ │ │ │ ├── ApiResponderTest.php │ │ │ ├── ProviderTest.php │ │ │ ├── ResponderTest.php │ │ │ └── TwigResponderTest.php │ │ ├── Routing/ │ │ │ ├── Factory/ │ │ │ │ ├── AttributesOperationRouteFactoryTest.php │ │ │ │ ├── OperationRouteFactoryTest.php │ │ │ │ ├── Resource/ │ │ │ │ │ └── ResourceRouteCollectionFactoryTest.php │ │ │ │ ├── RouteName/ │ │ │ │ │ └── OperationRouteNameFactoryTest.php │ │ │ │ └── RoutePath/ │ │ │ │ ├── BulkOperationRoutePathFactoryTest.php │ │ │ │ ├── CollectionOperationRoutePathFactoryTest.php │ │ │ │ ├── CreateOperationRoutePathFactoryTest.php │ │ │ │ ├── DeleteOperationRoutePathFactoryTest.php │ │ │ │ ├── OperationRoutePathFactoryTest.php │ │ │ │ ├── ShowOperationRoutePathFactoryTest.php │ │ │ │ └── UpdateOperationRoutePathFactoryTest.php │ │ │ ├── Loader/ │ │ │ │ └── ResourceLoaderTest.php │ │ │ └── RedirectHandlerTest.php │ │ ├── Security/ │ │ │ └── OperationAccessCheckerTest.php │ │ ├── Serializer/ │ │ │ └── State/ │ │ │ ├── DeserializeProviderTest.php │ │ │ └── SerializeProcessorTest.php │ │ ├── Session/ │ │ │ └── Flash/ │ │ │ └── FlashHelperTest.php │ │ ├── Validator/ │ │ │ ├── EventListener/ │ │ │ │ └── ValidationExceptionListenerTest.php │ │ │ ├── Exception/ │ │ │ │ └── ValidationExceptionTest.php │ │ │ └── State/ │ │ │ └── ValidateProviderTest.php │ │ └── Workflow/ │ │ └── OperationStateMachineTest.php │ ├── Translation/ │ │ ├── Provider/ │ │ │ └── ImmutableTranslationLocaleProviderTest.php │ │ └── TranslatableEntityLocaleAssignerTest.php │ ├── Twig/ │ │ └── Context/ │ │ └── Factory/ │ │ ├── ContextFactoryTest.php │ │ └── DefaultContextFactoryTest.php │ └── Winzou/ │ └── StateMachine/ │ └── OperationStateMachineTest.php └── tests/ ├── ApiTestCase.php ├── Application/ │ ├── .gitignore │ ├── bin/ │ │ └── console │ ├── composer.json │ ├── config/ │ │ ├── bootstrap.php │ │ ├── bundles.php │ │ ├── integration/ │ │ │ ├── fos_rest.yaml │ │ │ ├── jms_serializer.yaml │ │ │ ├── symfony_workflow.yaml │ │ │ └── winzou_state_machine.yaml │ │ ├── packages/ │ │ │ ├── doctrine.yaml │ │ │ ├── framework.yaml │ │ │ ├── messenger.yaml │ │ │ ├── routing.yaml │ │ │ ├── security.yaml │ │ │ ├── test/ │ │ │ │ ├── grids.yaml │ │ │ │ ├── sylius_grid.yaml │ │ │ │ └── twig.yaml │ │ │ ├── test_with_attributes/ │ │ │ │ └── sylius_resource.yaml │ │ │ ├── test_without_fosrest/ │ │ │ │ ├── grids.yaml │ │ │ │ ├── sylius_grid.yaml │ │ │ │ └── twig.yaml │ │ │ └── test_without_hateoas/ │ │ │ ├── fos_rest.yaml │ │ │ ├── grids.yaml │ │ │ ├── jms_serializer.yaml │ │ │ ├── sylius_grid.yaml │ │ │ └── twig.yaml │ │ ├── routes/ │ │ │ └── sylius_resource.yaml │ │ ├── routes.yaml │ │ ├── services/ │ │ │ └── integration/ │ │ │ └── gedmo.yaml │ │ ├── services.php │ │ ├── services.yaml │ │ ├── sylius/ │ │ │ ├── resources/ │ │ │ │ └── imports/ │ │ │ │ └── speaker.php │ │ │ └── resources.yaml │ │ ├── validator/ │ │ │ └── validation.yaml │ │ └── versioned_routing.yaml │ ├── public/ │ │ └── index.php │ ├── src/ │ │ ├── BoardGameBlog/ │ │ │ ├── Application/ │ │ │ │ ├── Command/ │ │ │ │ │ ├── CreateBoardGameCommand.php │ │ │ │ │ ├── CreateBoardGameCommandHandler.php │ │ │ │ │ ├── DeleteBoardGameCommand.php │ │ │ │ │ ├── DeleteBoardGameCommandHandler.php │ │ │ │ │ ├── UpdateBoardGameCommand.php │ │ │ │ │ └── UpdateBoardGameCommandHandler.php │ │ │ │ └── Query/ │ │ │ │ ├── FindBoardGameQuery.php │ │ │ │ └── FindBoardGameQueryHandler.php │ │ │ ├── Domain/ │ │ │ │ ├── Exception/ │ │ │ │ │ └── MissingBoardGameException.php │ │ │ │ ├── Model/ │ │ │ │ │ └── BoardGame.php │ │ │ │ ├── Repository/ │ │ │ │ │ └── BoardGameRepositoryInterface.php │ │ │ │ └── ValueObject/ │ │ │ │ ├── BoardGameId.php │ │ │ │ └── BoardGameName.php │ │ │ └── Infrastructure/ │ │ │ ├── Doctrine/ │ │ │ │ └── DoctrineBoardGameRepository.php │ │ │ ├── Foundry/ │ │ │ │ └── Factory/ │ │ │ │ └── BoardGameFactory.php │ │ │ ├── Sylius/ │ │ │ │ ├── Grid/ │ │ │ │ │ └── BoardGameGrid.php │ │ │ │ ├── Resource/ │ │ │ │ │ ├── BoardGameResource.php │ │ │ │ │ └── Mutator/ │ │ │ │ │ ├── BoardGameTemplatesDirMutator.php │ │ │ │ │ └── CreateBoardGameProcessorMutator.php │ │ │ │ └── State/ │ │ │ │ └── Http/ │ │ │ │ ├── Processor/ │ │ │ │ │ ├── CreateBoardGameProcessor.php │ │ │ │ │ ├── DeleteBoardGameProcessor.php │ │ │ │ │ └── UpdateBoardGameProcessor.php │ │ │ │ └── Provider/ │ │ │ │ ├── BoardGameCollectionProvider.php │ │ │ │ └── BoardGameItemProvider.php │ │ │ └── Symfony/ │ │ │ └── Form/ │ │ │ └── Type/ │ │ │ └── BoardGameType.php │ │ ├── Conference/ │ │ │ ├── Entity/ │ │ │ │ └── Speaker.php │ │ │ ├── Factory/ │ │ │ │ └── SpeakerFactory.php │ │ │ ├── Form/ │ │ │ │ └── SpeakerType.php │ │ │ └── Grid/ │ │ │ └── SpeakerGrid.php │ │ ├── Controller/ │ │ │ └── BookController.php │ │ ├── Entity/ │ │ │ ├── Author.php │ │ │ ├── BlogPost.php │ │ │ ├── Book.php │ │ │ ├── BookTranslation.php │ │ │ ├── BookTranslationInterface.php │ │ │ ├── ComicBook.php │ │ │ ├── CrudRoutes/ │ │ │ │ ├── BookWithAlias.php │ │ │ │ ├── BookWithCriteria.php │ │ │ │ ├── BookWithExcept.php │ │ │ │ ├── BookWithGrid.php │ │ │ │ ├── BookWithLegacyAttribute.php │ │ │ │ ├── BookWithOnly.php │ │ │ │ ├── BookWithPermission.php │ │ │ │ ├── BookWithRedirect.php │ │ │ │ ├── BookWithSection.php │ │ │ │ ├── BookWithTemplate.php │ │ │ │ └── BookWithVars.php │ │ │ ├── GedmoBaseExample.php │ │ │ ├── GedmoExtendedExample.php │ │ │ ├── LegacyBook.php │ │ │ ├── LegacyBookTranslation.php │ │ │ ├── PullRequest.php │ │ │ ├── Route/ │ │ │ │ ├── PublishBook.php │ │ │ │ ├── RegisterUserWithForm.php │ │ │ │ ├── RegisterUserWithFormOptions.php │ │ │ │ ├── ShowBook.php │ │ │ │ ├── ShowBookWithCriteria.php │ │ │ │ ├── ShowBookWithCsrfProtection.php │ │ │ │ ├── ShowBookWithGrid.php │ │ │ │ ├── ShowBookWithHost.php │ │ │ │ ├── ShowBookWithLegacyAttribute.php │ │ │ │ ├── ShowBookWithMethods.php │ │ │ │ ├── ShowBookWithOptions.php │ │ │ │ ├── ShowBookWithPermission.php │ │ │ │ ├── ShowBookWithPriority.php │ │ │ │ ├── ShowBookWithRepository.php │ │ │ │ ├── ShowBookWithRequirements.php │ │ │ │ ├── ShowBookWithSchemes.php │ │ │ │ ├── ShowBookWithSection.php │ │ │ │ ├── ShowBookWithSerializationGroups.php │ │ │ │ ├── ShowBookWithSerializationVersion.php │ │ │ │ ├── ShowBookWithTemplate.php │ │ │ │ ├── ShowBookWithVars.php │ │ │ │ ├── UpdateBookWithCustomEventName.php │ │ │ │ ├── UpdateBookWithRedirect.php │ │ │ │ ├── UpdateBookWithRedirectOptions.php │ │ │ │ └── UpdateBookWithReturnContent.php │ │ │ ├── ScienceBook.php │ │ │ ├── User.php │ │ │ └── Zone/ │ │ │ ├── Zone.php │ │ │ ├── ZoneInterface.php │ │ │ ├── ZoneMember.php │ │ │ └── ZoneMemberInterface.php │ │ ├── Factory/ │ │ │ ├── BookFactory.php │ │ │ ├── BookFactoryInterface.php │ │ │ ├── CustomBookFactory.php │ │ │ └── LegacyBookFactory.php │ │ ├── Form/ │ │ │ └── Type/ │ │ │ ├── AuthorType.php │ │ │ ├── BlogPostType.php │ │ │ ├── BookTranslationType.php │ │ │ ├── BookType.php │ │ │ ├── PullRequestType.php │ │ │ ├── RegisterType.php │ │ │ └── ScienceBookType.php │ │ ├── Foundry/ │ │ │ ├── Factory/ │ │ │ │ ├── AuthorFactory.php │ │ │ │ ├── BlogPostFactory.php │ │ │ │ ├── BookFactory.php │ │ │ │ ├── BookTranslationFactory.php │ │ │ │ ├── ComicBookFactory.php │ │ │ │ ├── PullRequestFactory.php │ │ │ │ └── ScienceBookFactory.php │ │ │ └── Story/ │ │ │ ├── DefaultBooksStory.php │ │ │ ├── DefaultComicBooksStory.php │ │ │ └── MoreBooksStory.php │ │ ├── Kernel.php │ │ ├── Repository/ │ │ │ ├── BookRepository.php │ │ │ ├── BookRepositoryInterface.php │ │ │ ├── ComicBookRepository.php │ │ │ ├── CustomBookRepository.php │ │ │ ├── LegacyBookRepository.php │ │ │ └── Tests/ │ │ │ └── Tmp/ │ │ │ └── .gitignore │ │ ├── Service/ │ │ │ ├── FirstAutowiredService.php │ │ │ ├── LegacyAutowiredFactoryService.php │ │ │ ├── LegacyAutowiredRepositoryService.php │ │ │ ├── LegacyAutowiredTranslatableFactoryService.php │ │ │ ├── NoInterfaceAutowiredService.php │ │ │ └── SecondAutowiredService.php │ │ ├── Shared/ │ │ │ ├── Application/ │ │ │ │ ├── Command/ │ │ │ │ │ ├── CommandBusInterface.php │ │ │ │ │ ├── CommandHandlerInterface.php │ │ │ │ │ └── CommandInterface.php │ │ │ │ └── Query/ │ │ │ │ ├── QueryBusInterface.php │ │ │ │ ├── QueryHandlerInterface.php │ │ │ │ └── QueryInterface.php │ │ │ ├── Domain/ │ │ │ │ ├── Repository/ │ │ │ │ │ └── RepositoryInterface.php │ │ │ │ └── ValueObject/ │ │ │ │ └── AggregateRootId.php │ │ │ └── Infrastructure/ │ │ │ ├── Symfony/ │ │ │ │ └── Messenger/ │ │ │ │ ├── MessengerCommandBus.php │ │ │ │ └── MessengerQueryBus.php │ │ │ └── Twig/ │ │ │ └── SyliusStateMachineExtension.php │ │ ├── Subscription/ │ │ │ ├── Entity/ │ │ │ │ ├── Subscription.php │ │ │ │ └── SubscriptionRepository.php │ │ │ ├── EventSubscriber/ │ │ │ │ └── SmokeSubscriptionEventsSubscriber.php │ │ │ ├── Factory/ │ │ │ │ └── SubscriptionFactory.php │ │ │ ├── Form/ │ │ │ │ └── Type/ │ │ │ │ └── SubscriptionType.php │ │ │ ├── Foundry/ │ │ │ │ ├── Factory/ │ │ │ │ │ └── SubscriptionFactory.php │ │ │ │ └── Story/ │ │ │ │ └── DefaultSubscriptionsStory.php │ │ │ ├── Grid/ │ │ │ │ └── SubscriptionGrid.php │ │ │ └── Twig/ │ │ │ └── Context/ │ │ │ └── Factory/ │ │ │ └── ShowSubscriptionContextFactory.php │ │ └── Tests/ │ │ ├── Controller/ │ │ │ ├── BlogPostApiTest.php │ │ │ ├── BoardGameUiTest.php │ │ │ ├── BookApiTest.php │ │ │ ├── ComicBookApiTest.php │ │ │ ├── GedmoApiTest.php │ │ │ ├── PullRequestApiTest.php │ │ │ ├── ScienceBookUiTest.php │ │ │ ├── SpeakerUiTest.php │ │ │ ├── SubscriptionJsonApiTest.php │ │ │ ├── SubscriptionUiTest.php │ │ │ └── SubscriptionXmlApiTest.php │ │ └── Validator/ │ │ └── TranslatableValidatorTest.php │ ├── templates/ │ │ ├── ScienceBook/ │ │ │ ├── create.html.twig │ │ │ ├── index.html.twig │ │ │ ├── show.html.twig │ │ │ └── update.html.twig │ │ ├── board_game/ │ │ │ └── show.html.twig │ │ ├── crud/ │ │ │ ├── create.html.twig │ │ │ ├── index.html.twig │ │ │ └── update.html.twig │ │ ├── grid/ │ │ │ ├── action/ │ │ │ │ ├── apply_transition.html.twig │ │ │ │ ├── delete.html.twig │ │ │ │ ├── show.html.twig │ │ │ │ └── update.html.twig │ │ │ └── bulk_action/ │ │ │ ├── apply_transition.html.twig │ │ │ └── delete.html.twig │ │ ├── layout/ │ │ │ └── _flashes.html.twig │ │ ├── layout.html.twig │ │ └── subscription/ │ │ └── show.html.twig │ └── translations/ │ └── messages.en.yaml └── Bundle/ ├── Command/ │ └── DebugResourceCommandTest.php ├── Configuration/ │ └── ConfigurationTest.php ├── Context/ │ ├── Initiator/ │ │ └── LegacyRequestContextInitiatorTest.php │ └── Option/ │ └── RequestConfigurationOptionTest.php ├── Controller/ │ ├── DisabledAuthorizationCheckerTest.php │ ├── EventDispatcherTest.php │ ├── FlashHelperTest.php │ ├── NewResourceFactoryTest.php │ ├── ParametersParserTest.php │ ├── ParametersTest.php │ ├── RedirectHandlerTest.php │ ├── RequestConfigurationFactoryTest.php │ ├── RequestConfigurationTest.php │ ├── ResourceControllerTest.php │ ├── ResourceDeleteHandlerTest.php │ ├── ResourceFormFactoryTest.php │ ├── ResourceUpdateHandlerTest.php │ ├── ResourcesCollectionProviderTest.php │ ├── ResourcesResolverTest.php │ ├── SingleResourceProviderTest.php │ ├── StateMachineTest.php │ ├── ViewHandlerTest.php │ └── WorkflowTest.php ├── DependencyInjection/ │ ├── Compiler/ │ │ ├── DoctrineTargetEntitiesResolverPassTest.php │ │ ├── Helper/ │ │ │ └── TargetEntitiesResolverTest.php │ │ ├── PagerfantaBridgePassTest.php │ │ ├── RegisterFormBuilderPassTest.php │ │ ├── RegisterFqcnControllersPassTest.php │ │ ├── RegisterResourceRepositoryPassTest.php │ │ ├── RegisterResourceStateMachinePassTest.php │ │ ├── RegisterResourcesPassTest.php │ │ ├── RegisterStateMachinePassTest.php │ │ ├── UnregisterFosRestDefinitionsPassTest.php │ │ ├── UnregisterHateoasDefinitionsPassTest.php │ │ └── WinzouStateMachinePassTest.php │ ├── Driver/ │ │ └── Exception/ │ │ ├── InvalidDriverExceptionTest.php │ │ └── UnknownDriverExceptionTest.php │ ├── Dummy/ │ │ ├── BookWithAliasResource.php │ │ ├── BookWithApplicationNameResource.php │ │ ├── DummyResource.php │ │ └── NoDriverResource.php │ ├── PagerfantaExtensionTest.php │ ├── SyliusResourceExtensionTest.php │ └── php/ │ └── empty_file.php ├── Doctrine/ │ ├── ODM/ │ │ └── PHPCR/ │ │ └── EventListener/ │ │ ├── DefaultParentListenerTest.php │ │ ├── NameFilterListenerTest.php │ │ └── NameResolverListenerTest.php │ └── ORM/ │ └── Form/ │ └── Builder/ │ └── DefaultFormBuilderTest.php ├── Event/ │ └── ResourceControllerEventTest.php ├── EventListener/ │ ├── AbstractDoctrineListenerTest.php │ ├── ODMRepositoryClassSubscriberTest.php │ ├── ORMMappedSuperClassSubscriberTest.php │ ├── ORMRepositoryClassSubscriberTest.php │ └── ORMTranslatableListenerTest.php ├── ExpressionLanguage/ │ ├── ExpressionLanguageTest.php │ └── NotNullExpressionFunctionProviderTest.php ├── Fixtures/ │ ├── AnimalInterface.php │ ├── Bear.php │ ├── BearInterface.php │ ├── ChildEntity.php │ ├── Fly.php │ ├── FlyInterface.php │ ├── MammalInterface.php │ ├── ParentEntity.php │ └── Resource.php ├── Form/ │ ├── DataTransformer/ │ │ ├── CollectionToStringTransformerTest.php │ │ ├── RecursiveTransformerTest.php │ │ └── ResourceToIdentifierTransformerTest.php │ ├── EventSubscriber/ │ │ └── AddCodeFormSubscriberTest.php │ ├── Extension/ │ │ ├── CollectionTypeExtensionTest.php │ │ └── HttpFoundation/ │ │ └── HttpFoundationRequestHandlerTest.php │ ├── Registry/ │ │ └── FormTypeRegistryTest.php │ └── Type/ │ ├── AbstractResourceTypeTest.php │ ├── ArchivableTypeTest.php │ ├── DefaultResourceTypeTest.php │ ├── FixedCollectionTypeTest.php │ ├── ResourceAutocompleteChoiceTypeTest.php │ ├── ResourceToIdentifierTypeTest.php │ └── ResourceTranslationsTypeTest.php ├── Grid/ │ ├── Controller/ │ │ └── ResourcesResolverTest.php │ ├── Parser/ │ │ └── OptionsParserTest.php │ ├── Renderer/ │ │ ├── TwigBulkActionGridRendererTest.php │ │ └── TwigGridRendererTest.php │ └── View/ │ ├── LegacyGridViewFactoryTest.php │ ├── ResourceGridViewFactoryTest.php │ └── ResourceGridViewTest.php ├── Provider/ │ └── RequestParameterProviderTest.php ├── Resource/ │ └── ResourceServicesTest.php ├── Routing/ │ ├── CrudRoutesAttributesLoaderTest.php │ ├── ResourceLoaderTest.php │ └── RoutesAttributesLoaderTest.php ├── Storage/ │ ├── CookieStorageTest.php │ └── SessionStorageTest.php ├── SyliusResourceBundleTest.php ├── Twig/ │ └── Context/ │ └── LegacyContextFactoryTest.php └── Validator/ ├── DisabledValidatorTest.php ├── EnabledValidatorTest.php └── UniqueWithinCollectionConstraintValidatorTest.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ * !/src !/composer.json !/ecs.php !/phpstan.neon !/phpstan-baseline.neon !/phpunit.xml.dist !/psalm.xml !/rector.php !/tests ================================================ FILE: .gitattributes ================================================ /.gitbook.yaml export-ignore /.gitattributes export-ignore /.github export-ignore /.gitignore export-ignore /.php_cs.dist export-ignore /docker-compose.yml export-ignore /Dockerfile export-ignore /ecs.php export-ignore /Makefile export-ignore /phpstan.neon export-ignore /phpstan-baseline.neon export-ignore /phpunit.xml.dist export-ignore /psalm.xml export-ignore /rector.php /src/Component/legacy/tests export-ignore /src/Component/tests export-ignore /src/Component/phpunit.xml.dist export-ignore /tests export-ignore ================================================ FILE: .gitbook.yaml ================================================ root: ./docs/ ================================================ FILE: .github/CODEOWNERS ================================================ * @Sylius/core-team ================================================ FILE: .github/CONTRIBUTING.md ================================================ Contributing Patches and BDD Methodology ======================================== Sylius is a Open Source project driven by the community. Join our amazing adventure and we promise to be nice and welcoming to everyone. Remember, you do not have to be a Symfony guru or even a programmer to help! You can learn [how to contribute the patches](http://docs.sylius.com/en/latest/contributing/code/patches.html) in our [Contributing Guide](http://docs.sylius.com/en/latest/contributing/index.html). Security Issues --------------- We treat security very seriously, you can read about security procedures [here](http://docs.sylius.com/en/latest/contributing/code/security.html). ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.md ================================================ --- name: "Bug Report 🐛" about: Report a problem or error --- **Sylius version affected**: 1.x.y **Description** **Steps to reproduce** **Possible Solution** ================================================ FILE: .github/ISSUE_TEMPLATE/documentation-issue.md ================================================ --- name: "Documentation Issue 📖" about: Report missing or bugged documentation --- **Sylius docs version**: 1.x / latest **Description** ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.md ================================================ --- name: Feature request ✅ about: Suggest an idea for a feature or improvement --- **Describe the proposed solution** **Describe alternatives you've considered** **Additional context** ================================================ FILE: .github/ISSUE_TEMPLATE/security-issue.md ================================================ --- name: "Security Issue 🔐" about: Please contact us at security@sylius.com --- If you think that you have found a security issue in Sylius, please do not use the issue tracker and do not post it publicly. Instead, all security issues must be sent to `security@sylius.com`. ================================================ FILE: .github/ISSUE_TEMPLATE/support-question.md ================================================ --- name: "Support Question 🚫" about: See http://docs.sylius.com/en/latest/support/index.html for questions about using Sylius --- We use GitHub issues only to discuss about Sylius bugs, new features and documentation. For this kind of questions about Sylius usage, please use any of the support alternatives described here: http://docs.sylius.com/en/latest/support/index.html Thank you! ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ | Q | A | ---------------- | ----- | Bug report? | no/yes | Feature request? | no/yes | BC Break report? | no/yes | RFC? | no/yes ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ | Q | A | --------------- | ----- | Bug fix? | no/yes | New feature? | no/yes | BC breaks? | no/yes | Deprecations? | no/yes | Related tickets | fixes #X, partially #Y, mentioned in #Z | License | MIT ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: composer directory: "/" schedule: interval: daily time: "10:00" open-pull-requests-limit: 10 ignore: - dependency-name: phpstan/phpstan versions: - 0.12.70 - 0.12.71 - 0.12.73 - 0.12.74 - 0.12.75 - 0.12.76 - 0.12.78 - 0.12.79 - 0.12.80 - 0.12.83 - dependency-name: vimeo/psalm versions: - 4.5.0 - 4.5.1 - 4.5.2 - 4.6.0 - 4.6.2 - 4.6.3 - dependency-name: babdev/pagerfanta-bundle versions: - 3.0.0 - dependency-name: phpstan/phpstan-webmozart-assert versions: - 0.12.10 - 0.12.11 - 0.12.9 ================================================ FILE: .github/workflows/build.yml ================================================ name: Build on: push: branches-ignore: - 'dependabot/**' - 'upmerge/**' pull_request: ~ release: types: [created] schedule: - cron: "0 1 * * 6" # Run at 1am every Saturday workflow_dispatch: ~ jobs: tests: runs-on: ubuntu-latest name: > PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, ORM ${{ matrix.orm }} ${{ matrix['behat-transliterator'] != '' && format(', Behat transliterator {0}', matrix['behat-transliterator']) || '' }} ${{ matrix['gedmo-doctrine-extensions'] != '' && format(', Gedmo {0}', matrix['gedmo-doctrine-extensions']) || '' }} ${{ matrix['fos-rest-bundle'] != '' && format(', FriendsOfSymfony Rest Bundle {0}', matrix['fos-rest-bundle']) || '' }} ${{ matrix['hateoas-bundle'] != '' && format(', Hateoas Bundle {0}', matrix['hateoas-bundle']) || '' }} ${{ matrix['jms-serializer-bundle'] != '' && format(', JMSSerializer Bundle {0}', matrix['jms-serializer-bundle']) || '' }} ${{ matrix['symfony-workflow'] != '' && format(', Symfony Workflow {0}', matrix['symfony-workflow']) || '' }} ${{ matrix['winzou-state-machine-bundle'] != '' && format(', winzou State Machine Bundle {0}', matrix['winzou-state-machine-bundle']) || '' }} strategy: fail-fast: false matrix: orm: ['2.*', '3.*'] php: ["8.2", "8.3"] behat-transliterator: [""] composer-flags: ['--no-scripts --prefer-stable --prefer-dist'] gedmo-doctrine-extensions: [""] fos-rest-bundle: [""] hateoas-bundle: [""] jms-serializer-bundle: [""] symfony: ["6.4.*", "7.4.*"] symfony-workflow: [""] winzou-state-machine-bundle: [""] include: - php: "8.4" orm: "3.*" symfony: "8.0.*" # with Behat transliterator and Gedmo Doctrine extensions optional packages - behat-transliterator: '^1.2' gedmo-doctrine-extensions: '^3.17.1' fos-rest-bundle: '^3.7' jms-serializer-bundle: '^5.5' php: "8.3" orm: "3.*" symfony: "7.4.*" # with FriendsOfSymfony Rest Bundle, JMS Serializer Bundle & and Hateoas Bundle optional packages - fos-rest-bundle: '^3.7' hateoas-bundle: '^2.5' jms-serializer-bundle: '^5.5' php: "8.3" orm: "3.*" symfony: "7.4.*" # with winzou State Machine Bundle optional package - winzou-state-machine-bundle: '^0.6.2' php: "8.3" orm: "3.*" symfony: "7.4.*" # with Symfony workflow optional package - symfony-workflow: '7.4.*' php: "8.3" orm: "3.*" symfony: "7.4.*" steps: - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: "${{ matrix.php }}" extensions: mongodb-1.21.0 coverage: none tools: 'composer:v2, flex' - name: Restrict Symfony version if: matrix.symfony != '' run: | composer config extra.symfony.require "${{ matrix.symfony }}" (cd src/Component && composer config extra.symfony.require "${{ matrix.symfony }}") - name: Restrict ORM version if: matrix.orm != '' run: | composer require --dev doctrine/orm "${{ matrix.orm }}" --no-update --no-scripts (cd src/Component && composer require --dev doctrine/orm "${{ matrix.orm }}" --no-update --no-scripts) - name: Install Behat transliterator if: matrix.behat-transliterator != '' run: | composer require --dev behat/transliterator "${{ matrix.behat-transliterator }}" --no-update --no-scripts - name: Install FriendsOfSymfony Rest Bundle if: matrix.fos-rest-bundle != '' run: | composer require --dev friendsofsymfony/rest-bundle "${{ matrix.fos-rest-bundle }}" --no-update --no-scripts - name: Install Gedmo Doctrine Extensions if: matrix.gedmo-doctrine-extensions != '' run: | composer require --dev gedmo/doctrine-extensions "${{ matrix.gedmo-doctrine-extensions }}" --no-update --no-scripts - name: Install Hateoas bundle if: matrix.hateoas-bundle != '' run: composer require --dev willdurand/hateoas-bundle "${{ matrix.hateoas-bundle }}" --no-update --no-scripts - name: Install JMS Serializer bundle if: matrix.jms-serializer-bundle != '' run: composer require --dev jms/serializer-bundle "${{ matrix.jms-serializer-bundle }}" --no-update --no-scripts - name: Install winzou State Machine Bundle if: matrix.winzou-state-machine-bundle != '' run: | composer require --dev winzou/state-machine-bundle "${{ matrix.winzou-state-machine-bundle }}" --no-update --no-scripts - name: Install Symfony workflow if: matrix.symfony-workflow != '' run: | composer require --dev symfony/workflow "${{ matrix.symfony-workflow }}" --no-update --no-scripts - name: Install MongoDB ODM package run: | composer require --dev doctrine/mongodb-odm-bundle "^5.0" --no-update --no-scripts (cd src/Component && composer require --dev doctrine/mongodb-odm "^2.8" --no-update --no-scripts) - name: Install dependencies run: | composer update ${{ matrix.composer-flags }} (cd src/Component && composer update ${{ matrix.composer-flags }}) - name: Prepare test application run: | (cd tests/Application && bin/console doctrine:schema:create) - name: Run PHPStan run: vendor/bin/phpstan analyse --ansi --memory-limit=-1 -c phpstan.neon src - name: Run analysis run: | composer analyse (cd src/Component && composer validate --strict) - name: Run PHPUnit tests run: vendor/bin/phpunit --colors=always - name: Run lint container run: (cd tests/Application && bin/console lint:container) - name: Run lint container without sylius/grid-bundle package run: | find tests/Application/src -type f -name '*Grid.php' -delete composer remove sylius/grid-bundle --no-scripts --dev (cd tests/Application && bin/console cache:clear --env=test_without_twig) (cd tests/Application && bin/console lint:container --env=test_without_twig) composer require "sylius/grid-bundle: ^1.13 || ^1.15@alpha" --no-scripts --dev tests_with_skeleton: runs-on: ubuntu-latest name: "Tests with the Symfony Skeleton PHP ${{ matrix.php }}${{ matrix.skeleton != '' && format(', Skeleton {0}', matrix.skeleton) || '' }}" strategy: fail-fast: false matrix: php: ["8.3"] skeleton: ["^6", "^7"] steps: - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: "${{ matrix.php }}" extensions: mongodb-1.21.0 coverage: none - name: Create-project with skeleton run: | set -x composer create-project --ansi "symfony/skeleton:${{ matrix.skeleton }}" skeleton_app cd skeleton_app composer config extra.symfony.allow-contrib true composer config repositories.local '{"type": "path", "url": "../", "options": {"symlink": true}}' --json composer require -W --ansi sylius/resource-bundle "*@dev" ================================================ FILE: .github/workflows/docker.yml ================================================ name: Build Docker Image on: push: branches-ignore: - 'dependabot/**' paths-ignore: - "*.md" pull_request: paths-ignore: - "*.md" schedule: - cron: "0 1 * * 6" # Run at 1am every Saturday workflow_dispatch: ~ jobs: build-image: name: Build Docker Image and Test Docker Compose env: DOCKER_BUILDKIT: 1 # Requires Latest Buildx in docker CLI COMPOSE_DOCKER_CLI_BUILD: 1 # Requires Latest Buildx in docker compose CLI strategy: fail-fast: false matrix: platform: [linux/amd64,linux/arm64] runs-on: ubuntu-latest steps: - name: Set Up QEMU uses: docker/setup-qemu-action@v2 - name: Set Up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Build Image uses: docker/build-push-action@v3 with: push: false platforms: ${{ matrix.platform }} cache-from: type=gha cache-to: type=gha load: true - name: Shutdown Default MySQL run: sudo service mysql stop - name: Checkout Code uses: actions/checkout@v3 - name: Analyse Package run: make analyse - name: Run Tests run: make test ================================================ FILE: .github/workflows/packages_split.yaml ================================================ name: 'Packages Split' on: workflow_dispatch: inputs: branch: description: 'Branch to checkout and split. Ignored if tag is specified. At least one required.' required: false type: string tag: description: 'Tag to checkout and split. Takes priority over branch. At least one required.' required: false type: string jobs: split_packages: name: Split ${{ matrix.package.repository }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: package: - directory: src/Component repository: Resource steps: - name: Split package uses: SyliusLabs/SplitPackageAction@v1.0 with: directory: ${{ matrix.package.directory }} repository: ${{ matrix.package.repository }} branch: ${{ github.event.inputs.branch }} tag: ${{ github.event.inputs.tag }} token: ${{ secrets.SPLIT_TOKEN }} ================================================ FILE: .github/workflows/upmerge_pr.yaml ================================================ name: Upmerge PR on: schedule: - cron: "0 2 * * *" workflow_dispatch: ~ permissions: contents: write pull-requests: write jobs: upmerge: runs-on: ubuntu-latest if: github.repository == 'Sylius/SyliusResourceBundle' name: "Upmerge PR" timeout-minutes: 5 strategy: fail-fast: false matrix: include: - base_branch: "1.14" target_branch: "1.15" steps: - uses: actions/checkout@v4 with: ref: ${{ matrix.target_branch }} - name: Reset upmerge branch run: | git fetch origin ${{ matrix.base_branch }}:${{ matrix.base_branch }} git reset --hard ${{ matrix.base_branch }} - name: Create Pull Request uses: peter-evans/create-pull-request@v4 with: token: ${{ secrets.SYLIUS_BOT_PAT }} title: '[UPMERGE] ${{ matrix.base_branch }} -> ${{ matrix.target_branch }}' body: | This PR has been generated automatically. For more details see [upmerge_pr.yaml](/Sylius/SyliusResourceBundle/blob/1.13/.github/workflows/upmerge_pr.yaml). **Remember!** The upmerge should always be merged with using `Merge pull request` button. In case of conflicts, please resolve them manually with usign the following commands: ``` git fetch upstream gh pr checkout git merge upstream/${{ matrix.target_branch }} -m "Resolve conflicts between ${{ matrix.base_branch }} and ${{ matrix.target_branch }}" ``` If you use other name for the upstream remote, please replace `upstream` with the name of your remote pointing to the `Sylius/SyliusResourceBundle` repository. Once the conflicts are resolved, please run `git merge --continue` and push the changes to this PR. branch: "upmerge/${{ matrix.base_branch }}_${{ matrix.target_branch }}" delete-branch: true base: ${{ matrix.target_branch }} ================================================ FILE: .gitignore ================================================ /vendor/ /composer.phar /composer.lock /symfony.lock /.phpunit.result.cache tests/Application/config/reference.php ================================================ FILE: CHANGELOG.md ================================================ ## CHANGELOG ## v1.13.1 (2025-09-24) #### Details - [#1037](https://github.com/Sylius/SyliusResourceBundle/pull/1037) Add missing event short name on apply state machine transition ([@loic425](https://github.com/loic425)) - [#1045](https://github.com/Sylius/SyliusResourceBundle/pull/1045) Fix - Add missing redirect & redirect arguments ([@loic425](https://github.com/loic425)) - [#1064](https://github.com/Sylius/SyliusResourceBundle/pull/1064) Fix missing behat transliterator package ([@loic425](https://github.com/loic425)) - [#1063](https://github.com/Sylius/SyliusResourceBundle/pull/1063) Add an error message for non existing repository method & suggest to use CreatePaginatorTrait ([@loic425](https://github.com/loic425)) ## v1.13.0 (2025-06-04) #### Details - [#1025](https://github.com/Sylius/SyliusResourceBundle/pull/1025) Fix implicit nullable parameter + add ECS rule to 1.12 ([@GSadee](https://github.com/GSadee)) - [#1024](https://github.com/Sylius/SyliusResourceBundle/pull/1024) Fix implicit nullable parameter + add ECS rule ([@GSadee](https://github.com/GSadee)) ## v1.13.0-BETA.1 (2025-05-28) #### Details - [#985](https://github.com/Sylius/SyliusResourceBundle/pull/985) Add resource name collection ([@loic425](https://github.com/loic425)) - [#986](https://github.com/Sylius/SyliusResourceBundle/pull/986) Init resource class list factory with attributes ([@loic425](https://github.com/loic425)) - [#987](https://github.com/Sylius/SyliusResourceBundle/pull/987) Resource route collection factory ([@loic425](https://github.com/loic425)) - [#997](https://github.com/Sylius/SyliusResourceBundle/pull/997) New resource loader ([@loic425](https://github.com/loic425)) - [#984](https://github.com/Sylius/SyliusResourceBundle/pull/984) Add Operation defaults trait ([@loic425](https://github.com/loic425)) - [#1003](https://github.com/Sylius/SyliusResourceBundle/pull/1003) Fix implicit nullable parameter on createRestView method ([@loic425](https://github.com/loic425)) - [#1006](https://github.com/Sylius/SyliusResourceBundle/pull/1006) Wrap `class_alias` calls in `class_exists` checks ([@quentint](https://github.com/quentint)) - [#1007](https://github.com/Sylius/SyliusResourceBundle/pull/1007) Gracefully handle rendering actions for non-resource grid view ([@vvasiloi](https://github.com/vvasiloi)) - [#1010](https://github.com/Sylius/SyliusResourceBundle/pull/1010) Add `#[Attribute]` annotation to validator constraints ([@Rafikooo](https://github.com/Rafikooo)) - [#1015](https://github.com/Sylius/SyliusResourceBundle/pull/1015) Allow to define a template in grid action options ([@loic425](https://github.com/loic425)) - [#1014](https://github.com/Sylius/SyliusResourceBundle/pull/1014) Add tests with skeleton ([@loic425](https://github.com/loic425)) - [#1016](https://github.com/Sylius/SyliusResourceBundle/pull/1016) Fix ClassReflection resource class resolving edge case ([@Pierstoval](https://github.com/Pierstoval)) - [#1021](https://github.com/Sylius/SyliusResourceBundle/pull/1021) [Maintenance] Exclude dependabot and upmerge push builds ([@NoResponseMate](https://github.com/NoResponseMate)) - [#1013](https://github.com/Sylius/SyliusResourceBundle/pull/1013) Add new SecurityProvider to allow managing access to resource ([@Prometee](https://github.com/Prometee)) - [#994](https://github.com/Sylius/SyliusResourceBundle/pull/994) Fix missing route name on operations attributes ([@loic425](https://github.com/loic425)) ## v1.13.0-ALPHA.3 (2025-03-12) #### Details - [#977](https://github.com/Sylius/SyliusResourceBundle/issues/977) Fix implicit nullable parameter ([@loic425](https://github.com/loic425)) - [#979](https://github.com/Sylius/SyliusResourceBundle/issues/979) [De] Show legacy resource metadata on de command when specified ([@loic425](https://github.com/loic425)) - [#990](https://github.com/Sylius/SyliusResourceBundle/issues/990) Fix Grid provider used in Request grid provider to allow using grid c… ([@loic425](https://github.com/loic425)) - [#991](https://github.com/Sylius/SyliusResourceBundle/issues/991) [CI] Add GitHub Action for creating upmerge PRs ([@GSadee](https://github.com/GSadee)) - [#993](https://github.com/Sylius/SyliusResourceBundle/issues/993) Add route requirements ([@loic425](https://github.com/loic425)) - [#995](https://github.com/Sylius/SyliusResourceBundle/issues/995) Custom notification message ([@loic425](https://github.com/loic425)) ## v1.13.0-ALPHA.2 (2025-01-21) #### Details - [#970](https://github.com/Sylius/SyliusResourceBundle/issues/970) [DX][Internal] use Foundry for comic books ([@loic425](https://github.com/loic425)) - [#973](https://github.com/Sylius/SyliusResourceBundle/issues/973) Fix Delete handling exception previous argument type ([@loic425](https://github.com/loic425)) - [#971](https://github.com/Sylius/SyliusResourceBundle/issues/971) [DX][Internal] Do not use Alice anymore ([@loic425](https://github.com/loic425)) - [#963](https://github.com/Sylius/SyliusResourceBundle/issues/963) [Doc] Documentation migration to SyliusStack using gitbook ([@Prometee](https://github.com/Prometee)) - [#975](https://github.com/Sylius/SyliusResourceBundle/issues/975) Add missing package and version attributes in deprecated element ([@GSadee](https://github.com/GSadee)) ## v1.13.0-ALPHA.1 (2025-01-17) #### Details - [#936](https://github.com/Sylius/SyliusResourceBundle/issues/936) [phpspec-2-phpunit] migration of tests (Model) ([@loic425](https://github.com/loic425)) - [#957](https://github.com/Sylius/SyliusResourceBundle/issues/957) Deprecate VariantWithNoOptionsValuesException ([@GSadee](https://github.com/GSadee)) - [#959](https://github.com/Sylius/SyliusResourceBundle/issues/959) Create LICENSE_OF_TRADEMARK_AND_LOGO ([@damonsson](https://github.com/damonsson)) - [#958](https://github.com/Sylius/SyliusResourceBundle/issues/958) Update LICENSE year ([@damonsson](https://github.com/damonsson)) - [#961](https://github.com/Sylius/SyliusResourceBundle/issues/961) Bump dependencies & Introduce prefer lowest on CI ([@loic425](https://github.com/loic425)) - [#968](https://github.com/Sylius/SyliusResourceBundle/issues/968) Add support for Doctrine ORM 3 ([@loic425](https://github.com/loic425)) - [#969](https://github.com/Sylius/SyliusResourceBundle/issues/969) [DX][Internal] Start using Foundry ([@loic425](https://github.com/loic425)) ## v1.12.2 (2025-05-28) #### Details - [#990](https://github.com/Sylius/SyliusResourceBundle/pull/990) Fix Grid provider used in Request grid provider to allow using grid c… ([@loic425](https://github.com/loic425)) - [#1010](https://github.com/Sylius/SyliusResourceBundle/pull/1010) Add `#[Attribute]` annotation to validator constraints ([@Rafikooo](https://github.com/Rafikooo)) - [#1014](https://github.com/Sylius/SyliusResourceBundle/pull/1014) Add tests with skeleton ([@loic425](https://github.com/loic425)) - [#1016](https://github.com/Sylius/SyliusResourceBundle/pull/1016) Fix ClassReflection resource class resolving edge case ([@Pierstoval](https://github.com/Pierstoval)) - [#994](https://github.com/Sylius/SyliusResourceBundle/pull/994) Fix missing route name on operations attributes ([@loic425](https://github.com/loic425)) ## v1.12.1 (2025-01-21) #### Details - [#936](https://github.com/Sylius/SyliusResourceBundle/issues/936) [phpspec-2-phpunit] migration of tests (Model) ([@loic425](https://github.com/loic425)) - [#958](https://github.com/Sylius/SyliusResourceBundle/issues/958) Update LICENSE year ([@damonsson](https://github.com/damonsson)) - [#975](https://github.com/Sylius/SyliusResourceBundle/issues/975) Add missing package and version attributes in deprecated element ([@GSadee](https://github.com/GSadee)) ## v1.12.0 (2024-10-08) ## v1.12.0-BETA.2 (2024-10-07) #### Details - [#948](https://github.com/Sylius/SyliusResourceBundle/issues/948) Remove usage of Symfony\Component\HttpKernel\DependencyInjection\Exte… ([@loic425](https://github.com/loic425)) - [#949](https://github.com/Sylius/SyliusResourceBundle/issues/949) [Composer] Bump sylius/grid-bundle dev dependency ([@GSadee](https://github.com/GSadee)) - [#873](https://github.com/Sylius/SyliusResourceBundle/issues/873) Move winzou state machine on optional requirements ([@loic425](https://github.com/loic425), [@GSadee](https://github.com/GSadee)) - [#951](https://github.com/Sylius/SyliusResourceBundle/issues/951) [Composer] Add GridBundle 1.13 beta in dev dependencies for SF7 builds ([@GSadee](https://github.com/GSadee)) ## v1.12.0-BETA.1 (2024-10-01) #### Details - [#945](https://github.com/Sylius/SyliusResourceBundle/issues/945) Restrict Doctrine orm package version ([@loic425](https://github.com/loic425)) - [#861](https://github.com/Sylius/SyliusResourceBundle/issues/861) [Maintenance] Deprecate unused configuration nodes ([@NoResponseMate](https://github.com/NoResponseMate)) ## v1.12.0-ALPHA.3 (2024-09-23) #### Details - [#941](https://github.com/Sylius/SyliusResourceBundle/issues/941) Fix Doctrine dependency on state processors ([@loic425](https://github.com/loic425)) - [#932](https://github.com/Sylius/SyliusResourceBundle/issues/932) [phpspec-2-phpunit] migration of tests (Humanizer) ([@loic425](https://github.com/loic425)) - [#928](https://github.com/Sylius/SyliusResourceBundle/issues/928) [phpspec-2-phpunit] migration of tests (Doctrine) ([@loic425](https://github.com/loic425)) - [#929](https://github.com/Sylius/SyliusResourceBundle/issues/929) [phpspec-2-phpunit] migration of tests (Exception) ([@loic425](https://github.com/loic425)) - [#930](https://github.com/Sylius/SyliusResourceBundle/issues/930) [phpspec-2-phpunit] migration of tests (Generator) ([@loic425](https://github.com/loic425)) - [#931](https://github.com/Sylius/SyliusResourceBundle/issues/931) [phpspec-2-phpunit] migration of tests (Grid) ([@loic425](https://github.com/loic425)) - [#942](https://github.com/Sylius/SyliusResourceBundle/issues/942) Doctrine requirement is now optional ([@loic425](https://github.com/loic425)) - [#933](https://github.com/Sylius/SyliusResourceBundle/issues/933) [phpspec-2-phpunit] migration of tests (Metadata) ([@loic425](https://github.com/loic425)) ## v1.12.0-ALPHA.2 (2024-09-12) #### Details - [#934](https://github.com/Sylius/SyliusResourceBundle/issues/934) Reproduce getting the translation repository by its interface use case ([@loic425](https://github.com/loic425)) - [#935](https://github.com/Sylius/SyliusResourceBundle/issues/935) Fix getting translation repository ([@loic425](https://github.com/loic425)) ## v1.12.0-ALPHA.1 (2024-09-09) #### Details - [#872](https://github.com/Sylius/SyliusResourceBundle/issues/872) Move fos rest, jms serializer and hateoas on optional requirements ([@loic425](https://github.com/loic425)) - [#863](https://github.com/Sylius/SyliusResourceBundle/issues/863) Adding support for Symfony 7 ([@loic425](https://github.com/loic425)) - [#924](https://github.com/Sylius/SyliusResourceBundle/issues/924) Remove Twig 2 support ([@loic425](https://github.com/loic425)) - [#926](https://github.com/Sylius/SyliusResourceBundle/issues/926) [Composer] Allow GridBundle ^v1.13@alpha ([@GSadee](https://github.com/GSadee)) ## v1.11.4 (2024-10-07) #### Details - [#948](https://github.com/Sylius/SyliusResourceBundle/issues/948) Remove usage of Symfony\Component\HttpKernel\DependencyInjection\Exte… ([@loic425](https://github.com/loic425)) - [#949](https://github.com/Sylius/SyliusResourceBundle/issues/949) [Composer] Bump sylius/grid-bundle dev dependency ([@GSadee](https://github.com/GSadee)) ## v1.11.3 (2024-09-23) #### Details - [#934](https://github.com/Sylius/SyliusResourceBundle/issues/934) Reproduce getting the translation repository by its interface use case ([@loic425](https://github.com/loic425)) - [#941](https://github.com/Sylius/SyliusResourceBundle/issues/941) Fix Doctrine dependency on state processors ([@loic425](https://github.com/loic425)) ## v1.11.2 (2024-09-06) #### Details - [#913](https://github.com/Sylius/SyliusResourceBundle/issues/913) [Docs] Disable providing data ([@loic425](https://github.com/loic425)) - [#918](https://github.com/Sylius/SyliusResourceBundle/issues/918) Fix grid limits on request grid provider ([@loic425](https://github.com/loic425)) - [#914](https://github.com/Sylius/SyliusResourceBundle/issues/914) [Docs] Disable processing data ([@loic425](https://github.com/loic425)) - [#925](https://github.com/Sylius/SyliusResourceBundle/issues/925) [Docs] Resource validation ([@loic425](https://github.com/loic425)) ## v1.11.1 (2024-09-02) #### Details - [#917](https://github.com/Sylius/SyliusResourceBundle/issues/917) Quick Fix ORM translatable listener for Symfony 5 ([@loic425](https://github.com/loic425)) ## v1.11.0 (2024-08-29) ## v1.11.0-RC.2 (2024-07-12) #### Details - [#900](https://github.com/Sylius/SyliusResourceBundle/issues/900) [Maintenance] Fix PHPStan errors related to class-string ([@lchrusciel](https://github.com/lchrusciel)) ## v1.11.0-RC.1 (2024-07-11) #### Details - [#874](https://github.com/Sylius/SyliusResourceBundle/issues/874) Remove PHP 8.0 support & not maintained Symfony versions ([@loic425](https://github.com/loic425)) - [#878](https://github.com/Sylius/SyliusResourceBundle/issues/878) Fix dependency injection namespace ([@loic425](https://github.com/loic425)) - [#883](https://github.com/Sylius/SyliusResourceBundle/issues/883) Fix form parameter bags accesing in request handler ([@NoResponseMate](https://github.com/NoResponseMate)) - [#895](https://github.com/Sylius/SyliusResourceBundle/issues/895) Move some routing factories into 2 sub-directories ([@loic425](https://github.com/loic425)) - [#896](https://github.com/Sylius/SyliusResourceBundle/issues/896) Move attributes operation route factory into sylius resource namespace ([@loic425](https://github.com/loic425)) - [#898](https://github.com/Sylius/SyliusResourceBundle/issues/898) [Maintenance] Fix builds ([@NoResponseMate](https://github.com/NoResponseMate)) - [#899](https://github.com/Sylius/SyliusResourceBundle/issues/899) Add missing experimental tags for new routing system ([@loic425](https://github.com/loic425)) ## v1.11.0-BETA.2 (2024-05-14) #### Details - [#871](https://github.com/Sylius/SyliusResourceBundle/issues/871) Fix state machine tag ([@Zales0123](https://github.com/Zales0123)) Working on Sylius/SyliusResourceBundle (branch 1.11) ## v1.11.0-BETA.1 (2024-04-19) #### Details - [#545](https://github.com/Sylius/SyliusResourceBundle/issues/545) Update phpspec/phpspec to 7.3 ([@dannyvw](https://github.com/dannyvw)) - [#607](https://github.com/Sylius/SyliusResourceBundle/issues/607) Allow jms/serializer-bundle ^5.0 ([@dannyvw](https://github.com/dannyvw)) - [#611](https://github.com/Sylius/SyliusResourceBundle/issues/611) Allow doctrine/collections ^2.0 ([@dannyvw](https://github.com/dannyvw)) - [#613](https://github.com/Sylius/SyliusResourceBundle/issues/613) Configure specific state machine component for a resource ([@loic425](https://github.com/loic425)) - [#634](https://github.com/Sylius/SyliusResourceBundle/issues/634) Allow Pagerfanta 4.0 ([@mbabker](https://github.com/mbabker)) - [#659](https://github.com/Sylius/SyliusResourceBundle/issues/659) Debug resource with FQCN ([@loic425](https://github.com/loic425)) - [#682](https://github.com/Sylius/SyliusResourceBundle/issues/682) [New docs] docs' pagination ([@loic425](https://github.com/loic425)) - [#687](https://github.com/Sylius/SyliusResourceBundle/issues/687) [New docs] Configure the resource name ([@loic425](https://github.com/loic425)) - [#689](https://github.com/Sylius/SyliusResourceBundle/issues/689) [New docs] Configure the resource plural name ([@loic425](https://github.com/loic425)) - [#696](https://github.com/Sylius/SyliusResourceBundle/issues/696) [CI] Add support for PHP 8.2 ([@loic425](https://github.com/loic425)) - [#706](https://github.com/Sylius/SyliusResourceBundle/issues/706) [Test app] Use Doctrine attributes ([@loic425](https://github.com/loic425)) - [#717](https://github.com/Sylius/SyliusResourceBundle/issues/717) Fix after upmerge ([@loic425](https://github.com/loic425)) - [#718](https://github.com/Sylius/SyliusResourceBundle/issues/718) Poc rebased ([@loic425](https://github.com/loic425)) - [#719](https://github.com/Sylius/SyliusResourceBundle/issues/719) Update licence ([@Rafikooo](https://github.com/Rafikooo)) - [#723](https://github.com/Sylius/SyliusResourceBundle/issues/723) Fix default templates dir ([@loic425](https://github.com/loic425)) - [#724](https://github.com/Sylius/SyliusResourceBundle/issues/724) Add flash from event on processor ([@lchrusciel](https://github.com/lchrusciel)) - [#726](https://github.com/Sylius/SyliusResourceBundle/issues/726) Update license ([@Zales0123](https://github.com/Zales0123)) - [#727](https://github.com/Sylius/SyliusResourceBundle/issues/727) Bulk update ([@loic425](https://github.com/loic425)) - [#728](https://github.com/Sylius/SyliusResourceBundle/issues/728) Psalm fix after upmerge ([@Zales0123](https://github.com/Zales0123)) - [#729](https://github.com/Sylius/SyliusResourceBundle/issues/729) [doc] Add missing new line to configure_your_operations.md example ([@diimpp](https://github.com/diimpp)) - [#730](https://github.com/Sylius/SyliusResourceBundle/issues/730) Grid aware operation ([@loic425](https://github.com/loic425)) - [#731](https://github.com/Sylius/SyliusResourceBundle/issues/731) Configure driver on resource attribute ([@loic425](https://github.com/loic425)) - [#732](https://github.com/Sylius/SyliusResourceBundle/issues/732) Remove unused template ([@loic425](https://github.com/loic425)) - [#733](https://github.com/Sylius/SyliusResourceBundle/issues/733) Move PHPUnit tests from bundle ([@loic425](https://github.com/loic425)) - [#738](https://github.com/Sylius/SyliusResourceBundle/issues/738) Fix Doctrine subscriber deprecations ([@loic425](https://github.com/loic425)) - [#740](https://github.com/Sylius/SyliusResourceBundle/issues/740) [Fix] Update delete path name to avoid route conflicts ([@loic425](https://github.com/loic425)) - [#741](https://github.com/Sylius/SyliusResourceBundle/issues/741) Add request to routing arguments ([@loic425](https://github.com/loic425)) - [#742](https://github.com/Sylius/SyliusResourceBundle/issues/742) Define simple vars on your operations ([@loic425](https://github.com/loic425)) - [#743](https://github.com/Sylius/SyliusResourceBundle/issues/743) Update rector/rector requirement from ^0.13.5 to ^0.18.2 ([@dependabot](https://github.com/dependabot)[[@bot](https://github.com/bot)]) - [#745](https://github.com/Sylius/SyliusResourceBundle/issues/745) Improve testing debug resource command ([@loic425](https://github.com/loic425)) - [#746](https://github.com/Sylius/SyliusResourceBundle/issues/746) Add POST http method on update, delete and bulk delete operation ([@loic425](https://github.com/loic425)) - [#747](https://github.com/Sylius/SyliusResourceBundle/issues/747) Fix name of file inside the comments for BookFactory ([@harikt](https://github.com/harikt)) - [#757](https://github.com/Sylius/SyliusResourceBundle/issues/757) Use AsResource attribute instead of reserved word Resource ([@loic425](https://github.com/loic425)) - [#758](https://github.com/Sylius/SyliusResourceBundle/issues/758) Remove Component namespace on action ([@loic425](https://github.com/loic425)) - [#759](https://github.com/Sylius/SyliusResourceBundle/issues/759) Remove Component namespace on annotation ([@loic425](https://github.com/loic425)) - [#760](https://github.com/Sylius/SyliusResourceBundle/issues/760) Remove Component namespace on context ([@loic425](https://github.com/loic425)) - [#761](https://github.com/Sylius/SyliusResourceBundle/issues/761) Remove Component namespace on Doctrine ([@loic425](https://github.com/loic425)) - [#762](https://github.com/Sylius/SyliusResourceBundle/issues/762) Remove Component namespace on state ([@loic425](https://github.com/loic425)) - [#764](https://github.com/Sylius/SyliusResourceBundle/issues/764) Fix routing with moved attributes ([@loic425](https://github.com/loic425)) - [#766](https://github.com/Sylius/SyliusResourceBundle/issues/766) Move some namespaces on Storage ([@loic425](https://github.com/loic425)) - [#767](https://github.com/Sylius/SyliusResourceBundle/issues/767) Move some namespaces on Symfony event dispatchers ([@loic425](https://github.com/loic425)) - [#768](https://github.com/Sylius/SyliusResourceBundle/issues/768) Move some namespaces on Exceptions ([@loic425](https://github.com/loic425)) - [#769](https://github.com/Sylius/SyliusResourceBundle/issues/769) Move some namespaces on Grid ([@loic425](https://github.com/loic425)) - [#770](https://github.com/Sylius/SyliusResourceBundle/issues/770) Move some namespaces on Humanizer ([@loic425](https://github.com/loic425)) - [#772](https://github.com/Sylius/SyliusResourceBundle/issues/772) Move some namespaces on Symfony event listener ([@loic425](https://github.com/loic425)) - [#773](https://github.com/Sylius/SyliusResourceBundle/issues/773) Fix response status when method is safe ([@loic425](https://github.com/loic425)) - [#774](https://github.com/Sylius/SyliusResourceBundle/issues/774) Move some namespaces on new Metadata ([@loic425](https://github.com/loic425)) - [#775](https://github.com/Sylius/SyliusResourceBundle/issues/775) Move some namespaces on Twig ([@loic425](https://github.com/loic425)) - [#777](https://github.com/Sylius/SyliusResourceBundle/issues/777) Move some namespaces on Factory ([@loic425](https://github.com/loic425)) - [#778](https://github.com/Sylius/SyliusResourceBundle/issues/778) Move some namespaces on Symfony expression language ([@loic425](https://github.com/loic425)) - [#779](https://github.com/Sylius/SyliusResourceBundle/issues/779) Phpunit tests for Symfony deserialize listener ([@loic425](https://github.com/loic425)) - [#780](https://github.com/Sylius/SyliusResourceBundle/issues/780) Init Deserialize provider ([@loic425](https://github.com/loic425)) - [#783](https://github.com/Sylius/SyliusResourceBundle/issues/783) Move some namespaces on Symfony forms, requests and responses ([@loic425](https://github.com/loic425)) - [#784](https://github.com/Sylius/SyliusResourceBundle/issues/784) Move some namespaces on Symfony routing, validator exceptions and workflow ([@loic425](https://github.com/loic425)) - [#785](https://github.com/Sylius/SyliusResourceBundle/issues/785) Init read provider ([@loic425](https://github.com/loic425)) - [#786](https://github.com/Sylius/SyliusResourceBundle/issues/786) Fix readme with supported branches ([@loic425](https://github.com/loic425)) - [#788](https://github.com/Sylius/SyliusResourceBundle/issues/788) Init flash processor ([@loic425](https://github.com/loic425)) - [#789](https://github.com/Sylius/SyliusResourceBundle/issues/789) Phpunit tests for Symfony respond listener ([@loic425](https://github.com/loic425)) - [#790](https://github.com/Sylius/SyliusResourceBundle/issues/790) Init respond processor ([@loic425](https://github.com/loic425)) - [#791](https://github.com/Sylius/SyliusResourceBundle/issues/791) Phpunit tests for Symfony form listener ([@loic425](https://github.com/loic425)) - [#792](https://github.com/Sylius/SyliusResourceBundle/issues/792) Phpunit tests for Symfony flash listener ([@loic425](https://github.com/loic425)) - [#793](https://github.com/Sylius/SyliusResourceBundle/issues/793) Init form provider ([@loic425](https://github.com/loic425)) - [#795](https://github.com/Sylius/SyliusResourceBundle/issues/795) Init factory provider ([@loic425](https://github.com/loic425)) - [#798](https://github.com/Sylius/SyliusResourceBundle/issues/798) Init validate provider ([@loic425](https://github.com/loic425)) - [#799](https://github.com/Sylius/SyliusResourceBundle/issues/799) PHPUnit tests for Symfony serialize listener ([@loic425](https://github.com/loic425)) - [#800](https://github.com/Sylius/SyliusResourceBundle/issues/800) Init serialize processor ([@loic425](https://github.com/loic425)) - [#801](https://github.com/Sylius/SyliusResourceBundle/issues/801) PHPUnit tests for Symfony write listener ([@loic425](https://github.com/loic425)) - [#803](https://github.com/Sylius/SyliusResourceBundle/issues/803) PHPUnit tests for Symfony add format listener ([@loic425](https://github.com/loic425)) - [#804](https://github.com/Sylius/SyliusResourceBundle/issues/804) Move some namespaces on metadata ([@loic425](https://github.com/loic425)) - [#805](https://github.com/Sylius/SyliusResourceBundle/issues/805) Move some namespaces on generator ([@loic425](https://github.com/loic425)) - [#806](https://github.com/Sylius/SyliusResourceBundle/issues/806) Move some namespaces on reflection ([@loic425](https://github.com/loic425)) - [#810](https://github.com/Sylius/SyliusResourceBundle/issues/810) [CI] Exclude builds for Symfony 6.4 and doctrine/persistence 2.0 ([@GSadee](https://github.com/GSadee), [@loic425](https://github.com/loic425)) - [#815](https://github.com/Sylius/SyliusResourceBundle/issues/815) PHPUnit tests for factory ([@loic425](https://github.com/loic425)) - [#816](https://github.com/Sylius/SyliusResourceBundle/issues/816) PHPUnit tests for context ([@loic425](https://github.com/loic425)) - [#817](https://github.com/Sylius/SyliusResourceBundle/issues/817) Add DI for Main controller, providers and processors ([@loic425](https://github.com/loic425)) - [#818](https://github.com/Sylius/SyliusResourceBundle/issues/818) Fix Phpspec errors ([@loic425](https://github.com/loic425)) - [#819](https://github.com/Sylius/SyliusResourceBundle/issues/819) Move some namespaces on model ([@loic425](https://github.com/loic425)) - [#820](https://github.com/Sylius/SyliusResourceBundle/issues/820) Move some namespaces on repository ([@loic425](https://github.com/loic425)) - [#821](https://github.com/Sylius/SyliusResourceBundle/issues/821) Fix serialize processor ([@loic425](https://github.com/loic425)) - [#822](https://github.com/Sylius/SyliusResourceBundle/issues/822) Fix flash processor ([@loic425](https://github.com/loic425)) - [#823](https://github.com/Sylius/SyliusResourceBundle/issues/823) Fix event dispatcher provider ([@loic425](https://github.com/loic425)) - [#824](https://github.com/Sylius/SyliusResourceBundle/issues/824) Add main controller ([@loic425](https://github.com/loic425)) - [#826](https://github.com/Sylius/SyliusResourceBundle/issues/826) Replace decorated by processor into bulk aware processor ([@loic425](https://github.com/loic425)) - [#827](https://github.com/Sylius/SyliusResourceBundle/issues/827) Fix respond processor ([@loic425](https://github.com/loic425)) - [#828](https://github.com/Sylius/SyliusResourceBundle/issues/828) Dispatch pre write event processor ([@loic425](https://github.com/loic425)) - [#829](https://github.com/Sylius/SyliusResourceBundle/issues/829) Dispatch post write event processor ([@loic425](https://github.com/loic425)) - [#830](https://github.com/Sylius/SyliusResourceBundle/issues/830) Write processor ([@loic425](https://github.com/loic425)) - [#831](https://github.com/Sylius/SyliusResourceBundle/issues/831) Move deserialize provider into Symfony namespace ([@loic425](https://github.com/loic425)) - [#832](https://github.com/Sylius/SyliusResourceBundle/issues/832) Rename event dispatcher provider to dispatch post read event provider ([@loic425](https://github.com/loic425)) - [#833](https://github.com/Sylius/SyliusResourceBundle/issues/833) Add resource metadata on Twig context ([@loic425](https://github.com/loic425)) - [#836](https://github.com/Sylius/SyliusResourceBundle/issues/836) Remove cache on metadata when debug is enabled ([@loic425](https://github.com/loic425)) - [#838](https://github.com/Sylius/SyliusResourceBundle/issues/838) Rename legacy tests directory ([@loic425](https://github.com/loic425)) - [#841](https://github.com/Sylius/SyliusResourceBundle/issues/841) Add support for PHP 8.3 (CI) ([@loic425](https://github.com/loic425)) - [#847](https://github.com/Sylius/SyliusResourceBundle/issues/847) Fix autowiring for legacy factory and repository ([@loic425](https://github.com/loic425)) - [#849](https://github.com/Sylius/SyliusResourceBundle/issues/849) Add local constraints ([@Wojdylak](https://github.com/Wojdylak)) - [#853](https://github.com/Sylius/SyliusResourceBundle/issues/853) [Docs] Inject factories (autowiring) ([@loic425](https://github.com/loic425)) - [#857](https://github.com/Sylius/SyliusResourceBundle/issues/857) Move state machine with bc-layer ([@loic425](https://github.com/loic425)) - [#858](https://github.com/Sylius/SyliusResourceBundle/issues/858) Move Winzou namespace ([@loic425](https://github.com/loic425)) - [#860](https://github.com/Sylius/SyliusResourceBundle/issues/860) Move translations' namespace ([@loic425](https://github.com/loic425)) - [#862](https://github.com/Sylius/SyliusResourceBundle/issues/862) [Maintenance] Add an exception for unavailable storages ([@NoResponseMate](https://github.com/NoResponseMate)) - [#864](https://github.com/Sylius/SyliusResourceBundle/issues/864) Improve legacy book factory to use more bc-layer classes ([@loic425](https://github.com/loic425)) ## v1.11.0-ALPHA.2 (2024-01-30) #### Details - [#538](https://github.com/Sylius/SyliusResourceBundle/issues/538) fix(flashbag): fixed addFlash in ControllerTrait for Symfony 6 ([@UlrichHP](https://github.com/UlrichHP)) - [#611](https://github.com/Sylius/SyliusResourceBundle/issues/611) Allow doctrine/collections ^2.0 ([@dannyvw](https://github.com/dannyvw)) - [#634](https://github.com/Sylius/SyliusResourceBundle/issues/634) Allow Pagerfanta 4.0 ([@mbabker](https://github.com/mbabker)) - [#729](https://github.com/Sylius/SyliusResourceBundle/issues/729) [doc] Add missing new line to configure_your_operations.md example ([@diimpp](https://github.com/diimpp)) - [#730](https://github.com/Sylius/SyliusResourceBundle/issues/730) Grid aware operation ([@loic425](https://github.com/loic425)) - [#731](https://github.com/Sylius/SyliusResourceBundle/issues/731) Configure driver on resource attribute ([@loic425](https://github.com/loic425)) - [#732](https://github.com/Sylius/SyliusResourceBundle/issues/732) Remove unused template ([@loic425](https://github.com/loic425)) - [#733](https://github.com/Sylius/SyliusResourceBundle/issues/733) Move PHPUnit tests from bundle ([@loic425](https://github.com/loic425)) - [#738](https://github.com/Sylius/SyliusResourceBundle/issues/738) Fix Doctrine subscriber deprecations ([@loic425](https://github.com/loic425)) - [#740](https://github.com/Sylius/SyliusResourceBundle/issues/740) [Fix] Update delete path name to avoid route conflicts ([@loic425](https://github.com/loic425)) - [#741](https://github.com/Sylius/SyliusResourceBundle/issues/741) Add request to routing arguments ([@loic425](https://github.com/loic425)) - [#742](https://github.com/Sylius/SyliusResourceBundle/issues/742) Define simple vars on your operations ([@loic425](https://github.com/loic425)) - [#743](https://github.com/Sylius/SyliusResourceBundle/issues/743) Update rector/rector requirement from ^0.13.5 to ^0.18.2 ([@dependabot](https://github.com/dependabot)[[@bot](https://github.com/bot)]) - [#745](https://github.com/Sylius/SyliusResourceBundle/issues/745) Improve testing debug resource command ([@loic425](https://github.com/loic425)) - [#746](https://github.com/Sylius/SyliusResourceBundle/issues/746) Add POST http method on update, delete and bulk delete operation ([@loic425](https://github.com/loic425)) - [#747](https://github.com/Sylius/SyliusResourceBundle/issues/747) Fix name of file inside the comments for BookFactory ([@harikt](https://github.com/harikt)) - [#757](https://github.com/Sylius/SyliusResourceBundle/issues/757) Use AsResource attribute instead of reserved word Resource ([@loic425](https://github.com/loic425)) - [#758](https://github.com/Sylius/SyliusResourceBundle/issues/758) Remove Component namespace on action ([@loic425](https://github.com/loic425)) - [#759](https://github.com/Sylius/SyliusResourceBundle/issues/759) Remove Component namespace on annotation ([@loic425](https://github.com/loic425)) - [#760](https://github.com/Sylius/SyliusResourceBundle/issues/760) Remove Component namespace on context ([@loic425](https://github.com/loic425)) - [#761](https://github.com/Sylius/SyliusResourceBundle/issues/761) Remove Component namespace on Doctrine ([@loic425](https://github.com/loic425)) - [#762](https://github.com/Sylius/SyliusResourceBundle/issues/762) Remove Component namespace on state ([@loic425](https://github.com/loic425)) - [#764](https://github.com/Sylius/SyliusResourceBundle/issues/764) Fix routing with moved attributes ([@loic425](https://github.com/loic425)) - [#765](https://github.com/Sylius/SyliusResourceBundle/issues/765) Missed typehint for `TranslatableFactoryInterface` ([@Rafikooo](https://github.com/Rafikooo)) - [#766](https://github.com/Sylius/SyliusResourceBundle/issues/766) Move some namespaces on Storage ([@loic425](https://github.com/loic425)) - [#767](https://github.com/Sylius/SyliusResourceBundle/issues/767) Move some namespaces on Symfony event dispatchers ([@loic425](https://github.com/loic425)) - [#769](https://github.com/Sylius/SyliusResourceBundle/issues/769) Move some namespaces on Grid ([@loic425](https://github.com/loic425)) - [#770](https://github.com/Sylius/SyliusResourceBundle/issues/770) Move some namespaces on Humanizer ([@loic425](https://github.com/loic425)) - [#772](https://github.com/Sylius/SyliusResourceBundle/issues/772) Move some namespaces on Symfony event listener ([@loic425](https://github.com/loic425)) - [#773](https://github.com/Sylius/SyliusResourceBundle/issues/773) Fix response status when method is safe ([@loic425](https://github.com/loic425)) - [#774](https://github.com/Sylius/SyliusResourceBundle/issues/774) Move some namespaces on new Metadata ([@loic425](https://github.com/loic425)) - [#775](https://github.com/Sylius/SyliusResourceBundle/issues/775) Move some namespaces on Twig ([@loic425](https://github.com/loic425)) - [#777](https://github.com/Sylius/SyliusResourceBundle/issues/777) Move some namespaces on Factory ([@loic425](https://github.com/loic425)) - [#778](https://github.com/Sylius/SyliusResourceBundle/issues/778) Move some namespaces on Symfony expression language ([@loic425](https://github.com/loic425)) - [#779](https://github.com/Sylius/SyliusResourceBundle/issues/779) Phpunit tests for Symfony deserialize listener ([@loic425](https://github.com/loic425)) - [#780](https://github.com/Sylius/SyliusResourceBundle/issues/780) Init Deserialize provider ([@loic425](https://github.com/loic425)) - [#783](https://github.com/Sylius/SyliusResourceBundle/issues/783) Move some namespaces on Symfony forms, requests and responses ([@loic425](https://github.com/loic425)) - [#784](https://github.com/Sylius/SyliusResourceBundle/issues/784) Move some namespaces on Symfony routing, validator exceptions and workflow ([@loic425](https://github.com/loic425)) - [#785](https://github.com/Sylius/SyliusResourceBundle/issues/785) Init read provider ([@loic425](https://github.com/loic425)) - [#786](https://github.com/Sylius/SyliusResourceBundle/issues/786) Fix readme with supported branches ([@loic425](https://github.com/loic425)) - [#788](https://github.com/Sylius/SyliusResourceBundle/issues/788) Init flash processor ([@loic425](https://github.com/loic425)) - [#789](https://github.com/Sylius/SyliusResourceBundle/issues/789) Phpunit tests for Symfony respond listener ([@loic425](https://github.com/loic425)) - [#790](https://github.com/Sylius/SyliusResourceBundle/issues/790) Init respond processor ([@loic425](https://github.com/loic425)) - [#791](https://github.com/Sylius/SyliusResourceBundle/issues/791) Phpunit tests for Symfony form listener ([@loic425](https://github.com/loic425)) - [#792](https://github.com/Sylius/SyliusResourceBundle/issues/792) Phpunit tests for Symfony flash listener ([@loic425](https://github.com/loic425)) - [#793](https://github.com/Sylius/SyliusResourceBundle/issues/793) Init form provider ([@loic425](https://github.com/loic425)) - [#795](https://github.com/Sylius/SyliusResourceBundle/issues/795) Init factory provider ([@loic425](https://github.com/loic425)) - [#798](https://github.com/Sylius/SyliusResourceBundle/issues/798) Init validate provider ([@loic425](https://github.com/loic425)) - [#799](https://github.com/Sylius/SyliusResourceBundle/issues/799) PHPUnit tests for Symfony serialize listener ([@loic425](https://github.com/loic425)) - [#800](https://github.com/Sylius/SyliusResourceBundle/issues/800) Init serialize processor ([@loic425](https://github.com/loic425)) - [#801](https://github.com/Sylius/SyliusResourceBundle/issues/801) PHPUnit tests for Symfony write listener ([@loic425](https://github.com/loic425)) - [#803](https://github.com/Sylius/SyliusResourceBundle/issues/803) PHPUnit tests for Symfony add format listener ([@loic425](https://github.com/loic425)) - [#804](https://github.com/Sylius/SyliusResourceBundle/issues/804) Move some namespaces on metadata ([@loic425](https://github.com/loic425)) - [#805](https://github.com/Sylius/SyliusResourceBundle/issues/805) Move some namespaces on generator ([@loic425](https://github.com/loic425)) - [#806](https://github.com/Sylius/SyliusResourceBundle/issues/806) Move some namespaces on reflection ([@loic425](https://github.com/loic425)) - [#809](https://github.com/Sylius/SyliusResourceBundle/issues/809) Add support for Symfony 6.4 ([@loic425](https://github.com/loic425)) - [#810](https://github.com/Sylius/SyliusResourceBundle/issues/810) [CI] Exclude builds for Symfony 6.4 and doctrine/persistence 2.0 ([@GSadee](https://github.com/GSadee), [@loic425](https://github.com/loic425)) - [#815](https://github.com/Sylius/SyliusResourceBundle/issues/815) PHPUnit tests for factory ([@loic425](https://github.com/loic425)) - [#816](https://github.com/Sylius/SyliusResourceBundle/issues/816) PHPUnit tests for context ([@loic425](https://github.com/loic425)) - [#817](https://github.com/Sylius/SyliusResourceBundle/issues/817) Add DI for Main controller, providers and processors ([@loic425](https://github.com/loic425)) - [#818](https://github.com/Sylius/SyliusResourceBundle/issues/818) Fix Phpspec errors ([@loic425](https://github.com/loic425)) - [#819](https://github.com/Sylius/SyliusResourceBundle/issues/819) Move some namespaces on model ([@loic425](https://github.com/loic425)) - [#820](https://github.com/Sylius/SyliusResourceBundle/issues/820) Move some namespaces on repository ([@loic425](https://github.com/loic425)) - [#821](https://github.com/Sylius/SyliusResourceBundle/issues/821) Fix serialize processor ([@loic425](https://github.com/loic425)) - [#822](https://github.com/Sylius/SyliusResourceBundle/issues/822) Fix flash processor ([@loic425](https://github.com/loic425)) - [#823](https://github.com/Sylius/SyliusResourceBundle/issues/823) Fix event dispatcher provider ([@loic425](https://github.com/loic425)) - [#824](https://github.com/Sylius/SyliusResourceBundle/issues/824) Add main controller ([@loic425](https://github.com/loic425)) - [#826](https://github.com/Sylius/SyliusResourceBundle/issues/826) Replace decorated by processor into bulk aware processor ([@loic425](https://github.com/loic425)) - [#827](https://github.com/Sylius/SyliusResourceBundle/issues/827) Fix respond processor ([@loic425](https://github.com/loic425)) - [#828](https://github.com/Sylius/SyliusResourceBundle/issues/828) Dispatch pre write event processor ([@loic425](https://github.com/loic425)) - [#829](https://github.com/Sylius/SyliusResourceBundle/issues/829) Dispatch post write event processor ([@loic425](https://github.com/loic425)) - [#830](https://github.com/Sylius/SyliusResourceBundle/issues/830) Write processor ([@loic425](https://github.com/loic425)) - [#831](https://github.com/Sylius/SyliusResourceBundle/issues/831) Move deserialize provider into Symfony namespace ([@loic425](https://github.com/loic425)) - [#832](https://github.com/Sylius/SyliusResourceBundle/issues/832) Rename event dispatcher provider to dispatch post read event provider ([@loic425](https://github.com/loic425)) - [#833](https://github.com/Sylius/SyliusResourceBundle/issues/833) Add resource metadata on Twig context ([@loic425](https://github.com/loic425)) - [#836](https://github.com/Sylius/SyliusResourceBundle/issues/836) Remove cache on metadata when debug is enabled ([@loic425](https://github.com/loic425)) - [#838](https://github.com/Sylius/SyliusResourceBundle/issues/838) Rename legacy tests directory ([@loic425](https://github.com/loic425)) - [#841](https://github.com/Sylius/SyliusResourceBundle/issues/841) Add support for PHP 8.3 (CI) ([@loic425](https://github.com/loic425)) ### v1.11.0-ALPHA.1 (2023-06-22) #### Details - [#493](https://github.com/Sylius/SyliusResourceBundle/issues/493) Add support for Doctrine persistence version 3.0 (@[@loic425](https://github.com/loic425)) - [#498](https://github.com/Sylius/SyliusResourceBundle/issues/498) Add services' aliases to improve autowiring experience ([@loic425](https://github.com/loic425)) - [#502](https://github.com/Sylius/SyliusResourceBundle/issues/502) Fix the build ([@loic425](https://github.com/loic425)) - [#503](https://github.com/Sylius/SyliusResourceBundle/issues/503) Init gitattributes file to preserve the planet ([@loic425](https://github.com/loic425)) - [#507](https://github.com/Sylius/SyliusResourceBundle/issues/507) Fix Symfony 6 Exception ([@mpysiak](https://github.com/mpysiak), [@lchrusciel](https://github.com/lchrusciel)) - [#510](https://github.com/Sylius/SyliusResourceBundle/issues/510) Fix declaring repository on resource when it is a service entity repository ([@loic425](https://github.com/loic425)) - [#530](https://github.com/Sylius/SyliusResourceBundle/issues/530) [DOCS] Change configuration to correct one () - [#545](https://github.com/Sylius/SyliusResourceBundle/issues/545) Update phpspec/phpspec to 7.3 ([@dannyvw](https://github.com/dannyvw)) - [#549](https://github.com/Sylius/SyliusResourceBundle/issues/549) Hotfix for Attributes routing system ([@loic425](https://github.com/loic425)) - [#550](https://github.com/Sylius/SyliusResourceBundle/issues/550) Fix build on 1.10 ([@loic425](https://github.com/loic425)) - [#551](https://github.com/Sylius/SyliusResourceBundle/issues/551) Flip back service ids and FQCN ([@loic425](https://github.com/loic425)) - [#589](https://github.com/Sylius/SyliusResourceBundle/issues/589) Fix the build on 1.10 branch ([@loic425](https://github.com/loic425)) - [#607](https://github.com/Sylius/SyliusResourceBundle/issues/607) Allow jms/serializer-bundle ^5.0 ([@dannyvw](https://github.com/dannyvw)) - [#613](https://github.com/Sylius/SyliusResourceBundle/issues/613) Configure specific state machine component for a resource ([@loic425](https://github.com/loic425)) - [#659](https://github.com/Sylius/SyliusResourceBundle/issues/659) Debug resource with FQCN ([@loic425](https://github.com/loic425)) - [#682](https://github.com/Sylius/SyliusResourceBundle/issues/682) [New docs] docs' pagination ([@loic425](https://github.com/loic425)) - [#687](https://github.com/Sylius/SyliusResourceBundle/issues/687) [New docs] Configure the resource name ([@loic425](https://github.com/loic425)) - [#689](https://github.com/Sylius/SyliusResourceBundle/issues/689) [New docs] Configure the resource plural name ([@loic425](https://github.com/loic425)) - [#694](https://github.com/Sylius/SyliusResourceBundle/issues/694) Quick fix for state machine workflow usage with Symfony 6.2 ([@loic425](https://github.com/loic425)) - [#696](https://github.com/Sylius/SyliusResourceBundle/issues/696) [CI] Add support for PHP 8.2 ([@loic425](https://github.com/loic425)) - [#700](https://github.com/Sylius/SyliusResourceBundle/issues/700) Fix missing pagerfanta ORM adapter error ([@loic425](https://github.com/loic425)) - [#701](https://github.com/Sylius/SyliusResourceBundle/issues/701) [HotFix] Flip id with alias on resource loader ([@loic425](https://github.com/loic425)) - [#706](https://github.com/Sylius/SyliusResourceBundle/issues/706) [Test app] Use Doctrine attributes ([@loic425](https://github.com/loic425)) - [#717](https://github.com/Sylius/SyliusResourceBundle/issues/717) Fix after upmerge ([@loic425](https://github.com/loic425)) - [#718](https://github.com/Sylius/SyliusResourceBundle/issues/718) Poc rebased ([@loic425](https://github.com/loic425)) - [#719](https://github.com/Sylius/SyliusResourceBundle/issues/719) Update licence ([@Rafikooo](https://github.com/Rafikooo)) - [#723](https://github.com/Sylius/SyliusResourceBundle/issues/723) Fix default templates dir ([@loic425](https://github.com/loic425)) - [#724](https://github.com/Sylius/SyliusResourceBundle/issues/724) Add flash from event on processor ([@lchrusciel](https://github.com/lchrusciel)) - [#726](https://github.com/Sylius/SyliusResourceBundle/issues/726) Update license ([@Zales0123](https://github.com/Zales0123)) - [#727](https://github.com/Sylius/SyliusResourceBundle/issues/727) Bulk update ([@loic425](https://github.com/loic425)) - [#728](https://github.com/Sylius/SyliusResourceBundle/issues/728) Psalm fix after upmerge ([@Zales0123](https://github.com/Zales0123)) ### v1.10.3 (2023-12-01) #### Details - [#589](https://github.com/Sylius/SyliusResourceBundle/issues/589) Fix the build on 1.10 branch ([@loic425](https://github.com/loic425)) - [#493](https://github.com/Sylius/SyliusResourceBundle/issues/493) Add support for Doctrine persistence version 3.0 (@[@loic425](https://github.com/loic425)) - [#701](https://github.com/Sylius/SyliusResourceBundle/issues/701) [HotFix] Flip id with alias on resource loader ([@loic425](https://github.com/loic425)) - [#700](https://github.com/Sylius/SyliusResourceBundle/issues/700) Fix missing pagerfanta ORM adapter error ([@loic425](https://github.com/loic425)) - [#694](https://github.com/Sylius/SyliusResourceBundle/issues/694) Quick fix for state machine workflow usage with Symfony 6.2 ([@loic425](https://github.com/loic425)) - [#538](https://github.com/Sylius/SyliusResourceBundle/issues/538) fix(flashbag): fixed addFlash in ControllerTrait for Symfony 6 ([@UlrichHP](https://github.com/UlrichHP)) - [#765](https://github.com/Sylius/SyliusResourceBundle/issues/765) Missed typehint for `TranslatableFactoryInterface` ([@Rafikooo](https://github.com/Rafikooo)) - [#809](https://github.com/Sylius/SyliusResourceBundle/issues/809) Add support for Symfony 6.4 ([@loic425](https://github.com/loic425)) ### v1.10.2 (2023-01-04) #### Details - [#510](https://github.com/Sylius/SyliusResourceBundle/issues/510) Fix declaring repository on resource when it is a service entity repository ([@loic425](https://github.com/loic425)) - [#530](https://github.com/Sylius/SyliusResourceBundle/issues/530) [DOCS] Change configuration to correct one ([@arti0090](https://github.com/arti0090)) - [#549](https://github.com/Sylius/SyliusResourceBundle/issues/549) Hotfix for Attributes routing system ([@loic425](https://github.com/loic425)) - [#550](https://github.com/Sylius/SyliusResourceBundle/issues/550) Fix build on 1.10 ([@loic425](https://github.com/loic425)) - [#551](https://github.com/Sylius/SyliusResourceBundle/issues/551) Flip back service ids and FQCN ([@loic425](https://github.com/loic425)) ### v1.10.1 (2022-12-05) #### Details - [#498](https://github.com/Sylius/SyliusResourceBundle/issues/498) Add services' aliases to improve autowiring experience ([@loic425](https://github.com/loic425)) - [#502](https://github.com/Sylius/SyliusResourceBundle/issues/502) Fix the build ([@loic425](https://github.com/loic425)) - [#503](https://github.com/Sylius/SyliusResourceBundle/issues/503) Init gitattributes file to preserve the planet ([@loic425](https://github.com/loic425)) - [#507](https://github.com/Sylius/SyliusResourceBundle/issues/507) Fix Symfony 6 Exception ([@mpysiak](https://github.com/mpysiak), [@lchrusciel](https://github.com/lchrusciel)) ### v1.10.0 (2022-10-31) #### Details - [#489](https://github.com/Sylius/SyliusResourceBundle/issues/489) Fix can apply a transition on workflow with a graph ([@loic425](https://github.com/loic425)) - [#492](https://github.com/Sylius/SyliusResourceBundle/issues/492) [Maintenance] Out-of-date phpstan/phpdoc-parser conflict removed, docker static analysis memory limit increased ([@Rafikooo](https://github.com/Rafikooo)) - [#495](https://github.com/Sylius/SyliusResourceBundle/issues/495) Inform about potential BC break after response code change ([@Zales0123](https://github.com/Zales0123)) ### v1.10.0-BETA.1 (2022-10-18) #### Details - [#341](https://github.com/Sylius/SyliusResourceBundle/issues/341) Dropping usage of Request->get ([@loic425](https://github.com/loic425), [@Zales0123](https://github.com/Zales0123)) - [#450](https://github.com/Sylius/SyliusResourceBundle/issues/450) Adjust when some runtime deprecation notices are triggered and use Symfony's trigger_deprecation() helper ([@mbabker](https://github.com/mbabker)) - [#467](https://github.com/Sylius/SyliusResourceBundle/issues/467) [README] Add development section and update links ([@lchrusciel](https://github.com/lchrusciel)) - [#478](https://github.com/Sylius/SyliusResourceBundle/issues/478) Add tests with grids ([@loic425](https://github.com/loic425)) - [#487](https://github.com/Sylius/SyliusResourceBundle/issues/487) Make CsrfTokenManager public ([@Zales0123](https://github.com/Zales0123)) - [#488](https://github.com/Sylius/SyliusResourceBundle/issues/488) Return 422 status code when the form fails ([@belmeopmenieuwesim](https://github.com/belmeopmenieuwesim), [@Zales0123](https://github.com/Zales0123)) ### v1.10.0-ALPHA.2 (2022-09-09) #### Details - [#446](https://github.com/Sylius/SyliusResourceBundle/issues/446) [Maintenance] Add flex support to global composer ([@lchrusciel](https://github.com/lchrusciel)) - [#454](https://github.com/Sylius/SyliusResourceBundle/issues/454) Add generic typehint to RepositoryInterface and FactoryInterface ([@MrSrsen](https://github.com/MrSrsen)) - [#458](https://github.com/Sylius/SyliusResourceBundle/issues/458) Basic configuration of Gitbook ([@Zales0123](https://github.com/Zales0123)) - [#459](https://github.com/Sylius/SyliusResourceBundle/issues/459) Fix coding standards to fix the build ([@Zales0123](https://github.com/Zales0123)) - [#460](https://github.com/Sylius/SyliusResourceBundle/issues/460) [Maintenance] Allow flex plugin during plugin installation ([@lchrusciel](https://github.com/lchrusciel)) - [#461](https://github.com/Sylius/SyliusResourceBundle/issues/461) [docker]Dockerized Resource Bundle ([@Ferror](https://github.com/Ferror), [@lchrusciel](https://github.com/lchrusciel)) - [#462](https://github.com/Sylius/SyliusResourceBundle/issues/462) Configure global symfony/flex plugin ([@Zales0123](https://github.com/Zales0123)) - [#465](https://github.com/Sylius/SyliusResourceBundle/issues/465) [Maintenance] Downgrade rector to fix build ([@lchrusciel](https://github.com/lchrusciel)) - [#466](https://github.com/Sylius/SyliusResourceBundle/issues/466) Update rector/rector requirement from ^0.12.20 to ^0.13.5 ([@dependabot](https://github.com/dependabot)[[@bot](https://github.com/bot)]) - [#470](https://github.com/Sylius/SyliusResourceBundle/issues/470) [CI] Fix the build for 1.10 ([@loic425](https://github.com/loic425)) - [#471](https://github.com/Sylius/SyliusResourceBundle/issues/471) [Symfony 6] Fix submitting a form ([@loic425](https://github.com/loic425)) - [#474](https://github.com/Sylius/SyliusResourceBundle/issues/474) Resource alias, always return 2 array items ([@Prometee](https://github.com/Prometee), [@lchrusciel](https://github.com/lchrusciel)) ### v1.10.0-ALPHA.1 (2022-05-16) #### TL;DR Drop Symfony 4 support, add Symfony 6 support 🚀 #### Details - [#379](https://github.com/Sylius/SyliusResourceBundle/issues/379) Fix setDeprecated deprecation ([@dannyvw](https://github.com/dannyvw)) - [#380](https://github.com/Sylius/SyliusResourceBundle/issues/380) Fix session deprecation ([@dannyvw](https://github.com/dannyvw)) - [#399](https://github.com/Sylius/SyliusResourceBundle/issues/399) [Symfony 6] Fix Kernel on test app ([@loic425](https://github.com/loic425)) - [#401](https://github.com/Sylius/SyliusResourceBundle/issues/401) [Symfony 6] Use storage factory id option on test app ([@loic425](https://github.com/loic425)) - [#402](https://github.com/Sylius/SyliusResourceBundle/issues/402) [Symfony 6] Fix http foundation request handler typehints ([@loic425](https://github.com/loic425)) - [#408](https://github.com/Sylius/SyliusResourceBundle/issues/408) [1.10] Correct branch alias to 1.10-dev ([@Zales0123](https://github.com/Zales0123)) - [#411](https://github.com/Sylius/SyliusResourceBundle/issues/411) [Symfony 6] Bump Psalm version from 4.7 to 4.22 ([@loic425](https://github.com/loic425)) - [#412](https://github.com/Sylius/SyliusResourceBundle/issues/412) [Symfony 6] Fix bootstrap on test app ([@loic425](https://github.com/loic425)) - [#413](https://github.com/Sylius/SyliusResourceBundle/issues/413) [Symfony 6] Fix setting deprecations on pagerfanta bridge pass ([@loic425](https://github.com/loic425)) - [#418](https://github.com/Sylius/SyliusResourceBundle/issues/418) [Symfony 6] Fix getting container on PHPUnit tests ([@loic425](https://github.com/loic425)) - [#419](https://github.com/Sylius/SyliusResourceBundle/issues/419) [Symfony 6] Fix routes on test app ([@loic425](https://github.com/loic425)) - [#426](https://github.com/Sylius/SyliusResourceBundle/issues/426) Add missing options on sylius route attribute ([@loic425](https://github.com/loic425)) - [#428](https://github.com/Sylius/SyliusResourceBundle/issues/428) Add support for Symfony 6 ([@loic425](https://github.com/loic425), [@lchrusciel](https://github.com/lchrusciel)) - [#430](https://github.com/Sylius/SyliusResourceBundle/issues/430) Drop Symfony 4 support ([@loic425](https://github.com/loic425)) - [#431](https://github.com/Sylius/SyliusResourceBundle/issues/431) Simplify Kernel on test app ([@loic425](https://github.com/loic425)) - [#433](https://github.com/Sylius/SyliusResourceBundle/issues/433) Remove session pass ([@loic425](https://github.com/loic425)) - [#434](https://github.com/Sylius/SyliusResourceBundle/issues/434) [Maintenance] Bump EasyCodingStandard dependency ([@lchrusciel](https://github.com/lchrusciel)) - [#435](https://github.com/Sylius/SyliusResourceBundle/issues/435) [Maintenance] Removal of Sf4.4 BC layer leftovers ([@lchrusciel](https://github.com/lchrusciel)) - [#437](https://github.com/Sylius/SyliusResourceBundle/issues/437) Remove is master request usage ([@loic425](https://github.com/loic425)) - [#439](https://github.com/Sylius/SyliusResourceBundle/issues/439) Fix Phpspec for symfony 6 ([@loic425](https://github.com/loic425)) - [#440](https://github.com/Sylius/SyliusResourceBundle/issues/440) Fix some other getting container deprecations ([@loic425](https://github.com/loic425)) - [#444](https://github.com/Sylius/SyliusResourceBundle/issues/444) [Symfony 6] Third solution to fix resource controllers ([@loic425](https://github.com/loic425)) - [#445](https://github.com/Sylius/SyliusResourceBundle/issues/445) [Maintenance] Drop Sf4 ACL on parameters class due to lack of its support ([@lchrusciel](https://github.com/lchrusciel)) ### v1.9.1 (2022-05-16) #### Details - [#407](https://github.com/Sylius/SyliusResourceBundle/issues/407) [1.8] Correct branch alias to 1.8-dev ([@Zales0123](https://github.com/Zales0123)) - [#409](https://github.com/Sylius/SyliusResourceBundle/issues/409) Document supported branches ([@Zales0123](https://github.com/Zales0123)) - [#416](https://github.com/Sylius/SyliusResourceBundle/issues/416) Revert "Bump Pagerfanta from 2.x to 3.x" ([@lchrusciel](https://github.com/lchrusciel)) - [#417](https://github.com/Sylius/SyliusResourceBundle/issues/417) Revert "Revert "Bump Pagerfanta from 2.x to 3.x"" ([@lchrusciel](https://github.com/lchrusciel)) - [#424](https://github.com/Sylius/SyliusResourceBundle/issues/424) Run lint container instead of smoke test for twig ([@loic425](https://github.com/loic425)) - [#425](https://github.com/Sylius/SyliusResourceBundle/issues/425) Run lint container for state machine instead of smoke tests ([@loic425](https://github.com/loic425)) - [#432](https://github.com/Sylius/SyliusResourceBundle/issues/432) [Maintenance] Correct branch alias to 1.9-dev ([@lchrusciel](https://github.com/lchrusciel)) - [#446](https://github.com/Sylius/SyliusResourceBundle/issues/446) [Maintenance] Simplify GitHub action workflow ([@lchrusciel](https://github.com/lchrusciel)) ### v1.9.0 (2022-04-07) #### TL;DR Stable 1.9.0 release 🎉🎉🎉 - PHP bumped to ^8.0 - PHP 7.4 syntaxt - Sylius Resource routes generated with PHP attributes - Support for Symfony Workflow - Simpler usage of new service entity repository - Multiple bug fixes and improvements #### Details - [#375](https://github.com/Sylius/SyliusResourceBundle/issues/375) Testing build with PHP 8.1 ([@loic425](https://github.com/loic425)) - [#378](https://github.com/Sylius/SyliusResourceBundle/issues/378) Fix phpdoc for getTranslations ([@dannyvw](https://github.com/dannyvw)) - [#381](https://github.com/Sylius/SyliusResourceBundle/issues/381) Bump Pagerfanta from 2.x to 3.x ([@mbabker](https://github.com/mbabker)) - [#403](https://github.com/Sylius/SyliusResourceBundle/issues/403) Add form attribute on SyliusCrudRoute ([@loic425](https://github.com/loic425)) - [#405](https://github.com/Sylius/SyliusResourceBundle/issues/405) Fix the build ([@loic425](https://github.com/loic425)) - [#406](https://github.com/Sylius/SyliusResourceBundle/issues/406) Allow to run GitHub actions manually + fix build on 1.8 ([@Zales0123](https://github.com/Zales0123)) ### v1.9.0-RC.1 (2022-02-28) #### Details - [#338](https://github.com/Sylius/SyliusResourceBundle/issues/338) Fix some Symfony Deprecations ([@dannyvw](https://github.com/dannyvw)) - [#373](https://github.com/Sylius/SyliusResourceBundle/issues/373) Fix the build after #338 ([@Zales0123](https://github.com/Zales0123)) ### v1.9.0-BETA.1 (2022-02-10) #### Details - [#365](https://github.com/Sylius/SyliusResourceBundle/issues/365) Fix route loaders ([@loic425](https://github.com/loic425)) ### v1.9.0-ALPHA.1 (2022-01-25) #### TL;DR - Bump required PHP version to ^8.0 and use PHP 7.4 syntax - Generate Sylius Resource routes with PHP attributes - Add support for Symfony Workflow #### Details - [#287](https://github.com/Sylius/SyliusResourceBundle/issues/287) Manage event response in show and index action to be able to redirect ([@maximehuran](https://github.com/maximehuran), [@Zales0123](https://github.com/Zales0123)) - [#298](https://github.com/Sylius/SyliusResourceBundle/issues/298) Allow use with Pagerfanta 3.0 ([@mbabker](https://github.com/mbabker)) - [#310](https://github.com/Sylius/SyliusResourceBundle/issues/310) Upgrade to GitHub-native Dependabot ([@dependabot-preview](https://github.com/dependabot-preview)[[@bot](https://github.com/bot)]) - [#328](https://github.com/Sylius/SyliusResourceBundle/issues/328) Use PHP 7.4 syntax ([@Zales0123](https://github.com/Zales0123)) - [#330](https://github.com/Sylius/SyliusResourceBundle/issues/330) Add a simple way to use the new service entity repository ([@loic425](https://github.com/loic425)) - [#332](https://github.com/Sylius/SyliusResourceBundle/issues/332) Change all MasterRequest calls to MainRequest ([@Roshyo](https://github.com/Roshyo), [@Zales0123](https://github.com/Zales0123)) - [#333](https://github.com/Sylius/SyliusResourceBundle/issues/333) Don't use deprecated Twig `spaceless` tag ([@stloyd](https://github.com/stloyd)) - [#334](https://github.com/Sylius/SyliusResourceBundle/issues/334) Sylius route with attributes ([@loic425](https://github.com/loic425)) - [#334](https://github.com/Sylius/SyliusResourceBundle/issues/334) Symfony workflow ([@loic425](https://github.com/loic425)) - [#340](https://github.com/Sylius/SyliusResourceBundle/issues/340) Allow jms/serializer-bundle 4 ([@dannyvw](https://github.com/dannyvw)) - [#343](https://github.com/Sylius/SyliusResourceBundle/issues/343) Change ECS config to php and run it ([@Zales0123](https://github.com/Zales0123)) - [#344](https://github.com/Sylius/SyliusResourceBundle/issues/344) Remove travis build status from README ([@GSadee](https://github.com/GSadee)) - [#345](https://github.com/Sylius/SyliusResourceBundle/issues/345) Update phpstan/phpstan requirement from 0.12.94 to 0.12.99 ([@dependabot](https://github.com/dependabot)[[@bot](https://github.com/bot)]) - [#347](https://github.com/Sylius/SyliusResourceBundle/issues/347) Update phpstan/phpstan-webmozart-assert requirement from 0.12.12 to 0.12.16 ([@dependabot](https://github.com/dependabot)[[@bot](https://github.com/bot)]) - [#348](https://github.com/Sylius/SyliusResourceBundle/issues/348) Update phpstan/phpstan-phpunit requirement from 0.12.18 to 0.12.22 ([@dependabot](https://github.com/dependabot)[[@bot](https://github.com/bot)]) - [#349](https://github.com/Sylius/SyliusResourceBundle/issues/349) Update winzou/state-machine-bundle requirement from ^0.5 to ^0.6 ([@dependabot](https://github.com/dependabot)[[@bot](https://github.com/bot)]) - [#353](https://github.com/Sylius/SyliusResourceBundle/issues/353) Require symfony/routing and symfony/http-foundation 4.4 and 5.4 ([@Zales0123](https://github.com/Zales0123)) - [#354](https://github.com/Sylius/SyliusResourceBundle/issues/354) Reactivate checking coding standard ([@loic425](https://github.com/loic425)) - [#356](https://github.com/Sylius/SyliusResourceBundle/issues/356) Add documentation for Routes with attributes ([@loic425](https://github.com/loic425)) - [#358](https://github.com/Sylius/SyliusResourceBundle/issues/358) Fix docs for crud routes with attributes ([@loic425](https://github.com/loic425)) - [#359](https://github.com/Sylius/SyliusResourceBundle/issues/359) Fix type of serialization groups ([@loic425](https://github.com/loic425)) ### v1.8.4 (2022-04-11) #### Details - [#416](https://github.com/Sylius/SyliusResourceBundle/issues/416) Revert "Bump Pagerfanta from 2.x to 3.x" ([@lchrusciel](https://github.com/lchrusciel)) ### v1.8.3 (2022-04-07) #### Details - [#375](https://github.com/Sylius/SyliusResourceBundle/issues/375) Testing build with PHP 8.1 ([@loic425](https://github.com/loic425)) - [#381](https://github.com/Sylius/SyliusResourceBundle/issues/381) Bump Pagerfanta from 2.x to 3.x ([@mbabker](https://github.com/mbabker)) - [#406](https://github.com/Sylius/SyliusResourceBundle/issues/406) Allow to run GitHub actions manually + fix build on 1.8 ([@Zales0123](https://github.com/Zales0123)) ### v1.8.2 (2021-04-08) #### Details - [#304](https://github.com/Sylius/SyliusResourceBundle/issues/304) Fix doctrine extensions version on component ([@loic425](https://github.com/loic425)) - [#303](https://github.com/Sylius/SyliusResourceBundle/issues/303) fix namespace of `ConfigurationTest` ([@bendavies](https://github.com/bendavies)) - [#302](https://github.com/Sylius/SyliusResourceBundle/issues/302) Fix namespace of `WinzouStateMachinePassTest` ([@bendavies](https://github.com/bendavies)) - [#301](https://github.com/Sylius/SyliusResourceBundle/issues/301) Update phpstan/phpstan requirement from 0.12.82 to 0.12.83 ([@dependabot-preview](https://github.com/dependabot-preview)[[@bot](https://github.com/bot)]) - [#300](https://github.com/Sylius/SyliusResourceBundle/issues/300) Update vimeo/psalm requirement from 4.6.4 to 4.7.0 ([@dependabot-preview](https://github.com/dependabot-preview)[[@bot](https://github.com/bot)]) ### v1.8.1 (2021-03-19) #### Details - [#297](https://github.com/Sylius/SyliusResourceBundle/issues/297) Skip registering controllers as a services if there is no custom class defined ([@pamil](https://github.com/pamil)) ### v1.8.0 (2021-03-19) #### TL;DR - Added support for PHP 8 - Removed StofDoctrineExtensionsBundle from dependencies - Remvoed support for winzou/state-machine-bundle <0.5 #### Details - [#210](https://github.com/Sylius/SyliusResourceBundle/issues/210) Add compatibility with PHP 8 ([@pamil](https://github.com/pamil)) - [#247](https://github.com/Sylius/SyliusResourceBundle/issues/247) Fix wrong licence on test app's kernel ([@loic425](https://github.com/loic425)) - [#255](https://github.com/Sylius/SyliusResourceBundle/issues/255) Add autowire for resource Controllers ([@AdamKasp](https://github.com/AdamKasp), [@lchrusciel](https://github.com/lchrusciel)) - [#259](https://github.com/Sylius/SyliusResourceBundle/issues/259) [Minor] Add symfony.lock to git ignore ([@lchrusciel](https://github.com/lchrusciel)) - [#264](https://github.com/Sylius/SyliusResourceBundle/issues/264) Fix the build ([@pamil](https://github.com/pamil)) - [#283](https://github.com/Sylius/SyliusResourceBundle/issues/283) Remove StofDoctrineExtensionsBundle and replace it with GedmoDoctrineExtensions ([@pamil](https://github.com/pamil)) - [#285](https://github.com/Sylius/SyliusResourceBundle/issues/285) Drop winzou/state-machine-bundle <0.5 ([@pamil](https://github.com/pamil)) ### v1.7.1 (2020-12-09) #### Details - [#243](https://github.com/Sylius/SyliusResourceBundle/issues/243) Add back winzou/state-machine to required packages of the component ([@pamil](https://github.com/pamil)) ### v1.7.0 (2020-12-09) These are complete release notes summing up all BETA and RC releases. #### TL;DR - Bumped up requirements from PHP 7.2 to PHP 7.3 - Dropped support for Symfony ^3.4, added support for Symfony ^5.1 - Added support for `doctrine/doctrine-bundle` in version `^2.0` - Added support for `winzou/state-machine-bundle` in versions `^0.4.3` and `^0.5` - Bumped up `friendsofsymfony/rest-bundle` requirements from `^2.1` to `^3.0` - Bumped up `jms/serializer-bundle` requirements from `^2.0` to `^3.5` - Bumped up `willdurand/hateoas-bundle` requirements from `^1.2` to `^2.0` - Removed the usage of deprecated `doctrine/inflector` API, added support for version `^2.0` - Replaced `white-october/pagerfanta-bundle:^1.0` with `babdev/pagerfanta-bundle:^2.5` - Deduplicated repositories retrieved from the service container and from the object manager #### Details - [#114](https://github.com/Sylius/SyliusResourceBundle/issues/114) Updating composer dependencies ([@mamazu](https://github.com/mamazu)) - [#117](https://github.com/Sylius/SyliusResourceBundle/issues/117) Fix extended types deprecation ([@dannyvw](https://github.com/dannyvw)) - [#119](https://github.com/Sylius/SyliusResourceBundle/issues/119) Update composer dependencies ([@pamil](https://github.com/pamil), [@dannyvw](https://github.com/dannyvw)) - [#124](https://github.com/Sylius/SyliusResourceBundle/issues/124) Replace deprecated doctrine object manager ([@loic425](https://github.com/loic425)) - [#125](https://github.com/Sylius/SyliusResourceBundle/issues/125) Replace deprecated doctrine object repository ([@loic425](https://github.com/loic425)) - [#126](https://github.com/Sylius/SyliusResourceBundle/issues/126) Replace dbal types ([@loic425](https://github.com/loic425)) - [#127](https://github.com/Sylius/SyliusResourceBundle/issues/127) Fix phpspec tests on DefaultFormBuilder ([@loic425](https://github.com/loic425)) - [#128](https://github.com/Sylius/SyliusResourceBundle/issues/128) Lock static analysis tools versions & fix the build ([@pamil](https://github.com/pamil)) - [#129](https://github.com/Sylius/SyliusResourceBundle/issues/129) Add support for PHP 7.4 and Symfony 4.4 ([@pamil](https://github.com/pamil), [@dannyvw](https://github.com/dannyvw)) - [#130](https://github.com/Sylius/SyliusResourceBundle/issues/130) Remove deprecated templating configuration ([@dannyvw](https://github.com/dannyvw)) - [#131](https://github.com/Sylius/SyliusResourceBundle/issues/131) Allow twig 3.x ([@dannyvw](https://github.com/dannyvw)) - [#132](https://github.com/Sylius/SyliusResourceBundle/issues/132) Fix controller deprecation ([@dannyvw](https://github.com/dannyvw)) - [#133](https://github.com/Sylius/SyliusResourceBundle/issues/133) Fix testing multiple Symfony versions, add build for 5.0 & remove support for <4.4 ([@pamil](https://github.com/pamil)) - [#135](https://github.com/Sylius/SyliusResourceBundle/issues/135) Allow for DoctrineBundle ^2.0 ([@pamil](https://github.com/pamil)) - [#136](https://github.com/Sylius/SyliusResourceBundle/issues/136) Remove unneccessary dependency on winzou/state-machine in the component ([@pamil](https://github.com/pamil)) - [#138](https://github.com/Sylius/SyliusResourceBundle/issues/138) Remove legacy di configuration ([@dannyvw](https://github.com/dannyvw)) - [#139](https://github.com/Sylius/SyliusResourceBundle/issues/139) Fix event dispatcher deprecation ([@dannyvw](https://github.com/dannyvw)) - [#141](https://github.com/Sylius/SyliusResourceBundle/issues/141) Upgrade to PHPStan 0.12 ([@GSadee](https://github.com/GSadee)) - [#142](https://github.com/Sylius/SyliusResourceBundle/issues/142) Github repository configuration from Sylius/Sylius ([@CoderMaggie](https://github.com/CoderMaggie)) - [#143](https://github.com/Sylius/SyliusResourceBundle/issues/143) [Maintenance] Updated branch alias ([@lchrusciel](https://github.com/lchrusciel)) - [#144](https://github.com/Sylius/SyliusResourceBundle/issues/144) [Maintenance] Update github templates ([@lchrusciel](https://github.com/lchrusciel)) - [#151](https://github.com/Sylius/SyliusResourceBundle/issues/151) [Maintenance] Bump ApiTestCase to v5.0 ([@lchrusciel](https://github.com/lchrusciel)) - [#159](https://github.com/Sylius/SyliusResourceBundle/issues/159) Remove duplicated docblocks ([@GSadee](https://github.com/GSadee)) - [#160](https://github.com/Sylius/SyliusResourceBundle/issues/160) ResourceBundle documentation extracted to its repository ([@SirDomin](https://github.com/SirDomin)) - [#161](https://github.com/Sylius/SyliusResourceBundle/issues/161) [HOTFIX] Conflict with amphp/amp 2.4.3 ([@lchrusciel](https://github.com/lchrusciel)) - [#162](https://github.com/Sylius/SyliusResourceBundle/issues/162) [Documentation] Fix index menu ([@SirDomin](https://github.com/SirDomin)) - [#163](https://github.com/Sylius/SyliusResourceBundle/issues/163) [HOTFIX] Conflict with the newest amphp/amp ([@lchrusciel](https://github.com/lchrusciel)) - [#165](https://github.com/Sylius/SyliusResourceBundle/issues/165) Fix the build ([@pamil](https://github.com/pamil)) - [#167](https://github.com/Sylius/SyliusResourceBundle/issues/167) Upgrade rest bundle ([@loic425](https://github.com/loic425)) - [#168](https://github.com/Sylius/SyliusResourceBundle/issues/168) [Docs] Serialization groups of the elements in a paginated collection ([@vvasiloi](https://github.com/vvasiloi)) - [#172](https://github.com/Sylius/SyliusResourceBundle/issues/172) Check if form is submitted on resource creation/edition ([@loic425](https://github.com/loic425)) - [#173](https://github.com/Sylius/SyliusResourceBundle/issues/173) Remove rest dependencies ([@loic425](https://github.com/loic425), [@pamil](https://github.com/pamil)) - [#175](https://github.com/Sylius/SyliusResourceBundle/issues/175) Upgrade PagerfantaBundle to new version with B/C layer ([@mbabker](https://github.com/mbabker)) - [#177](https://github.com/Sylius/SyliusResourceBundle/issues/177) Pagerfanta updates ([@mbabker](https://github.com/mbabker)) - [#178](https://github.com/Sylius/SyliusResourceBundle/issues/178) When using winzouStateMachineBundle 0.4, the old named services are aliases, so need to be marked public as well ([@mbabker](https://github.com/mbabker), [@pamil](https://github.com/pamil)) - [#181](https://github.com/Sylius/SyliusResourceBundle/issues/181) Fix build ([@loic425](https://github.com/loic425)) - [#182](https://github.com/Sylius/SyliusResourceBundle/issues/182) Testing several state machine versions ([@loic425](https://github.com/loic425)) - [#187](https://github.com/Sylius/SyliusResourceBundle/issues/187) Symfony 5 support ([@loic425](https://github.com/loic425)) - [#189](https://github.com/Sylius/SyliusResourceBundle/issues/189) Require webmozart/assert as it's used by the bundle code ([@pamil](https://github.com/pamil)) - [#190](https://github.com/Sylius/SyliusResourceBundle/issues/190) Fix errors reported by static analysis tools ([@pamil](https://github.com/pamil)) - [#191](https://github.com/Sylius/SyliusResourceBundle/issues/191) Normalise composer.json (with ergebnis/composer-normalize) ([@pamil](https://github.com/pamil)) - [#192](https://github.com/Sylius/SyliusResourceBundle/issues/192) Remove conflict with amphp/amp ([@pamil](https://github.com/pamil)) - [#193](https://github.com/Sylius/SyliusResourceBundle/issues/193) Bump up requirements to PHP ^7.3 ([@pamil](https://github.com/pamil)) - [#194](https://github.com/Sylius/SyliusResourceBundle/issues/194) Fix deprecations and errors while running PHPUnit ([@pamil](https://github.com/pamil)) - [#195](https://github.com/Sylius/SyliusResourceBundle/issues/195) Upgrade to Psalm v3.17.1 ([@pamil](https://github.com/pamil)) - [#196](https://github.com/Sylius/SyliusResourceBundle/issues/196) Remove unnecessary dev dependency on "polishsymfonycommunity/symfony-mocker-container" ([@pamil](https://github.com/pamil)) - [#197](https://github.com/Sylius/SyliusResourceBundle/issues/197) Make RegisterFormBuilderPass accept multiple tags on a single service ([@pamil](https://github.com/pamil)) - [#198](https://github.com/Sylius/SyliusResourceBundle/issues/198) Bump up minimal requirements to Symfony ^5.1 ([@pamil](https://github.com/pamil)) - [#199](https://github.com/Sylius/SyliusResourceBundle/issues/199) Update the year in LICENSE file ([@ValentineJester](https://github.com/ValentineJester)) - [#200](https://github.com/Sylius/SyliusResourceBundle/issues/200) Use HTTPS instead of HTTP in links in composer.json ([@ValentineJester](https://github.com/ValentineJester)) - [#201](https://github.com/Sylius/SyliusResourceBundle/issues/201) Remove outdated persistence backends from the documentation ([@ValentineJester](https://github.com/ValentineJester)) - [#202](https://github.com/Sylius/SyliusResourceBundle/issues/202) Remove winzou state machine dependency ([@loic425](https://github.com/loic425)) - [#204](https://github.com/Sylius/SyliusResourceBundle/issues/204) Replace AbstractController with ControllerTrait & ContainerAwareInterface ([@pamil](https://github.com/pamil)) - [#206](https://github.com/Sylius/SyliusResourceBundle/issues/206) Remove twig bundle dependency ([@loic425](https://github.com/loic425)) - [#207](https://github.com/Sylius/SyliusResourceBundle/issues/207) Bump doctrine/persistance version ([@dotdevru](https://github.com/dotdevru)) - [#209](https://github.com/Sylius/SyliusResourceBundle/issues/209) [Travis] Use symfony/flex ^1.10 instead of dev-master ([@pamil](https://github.com/pamil)) - [#211](https://github.com/Sylius/SyliusResourceBundle/issues/211) Upgrade to Psalm 4 ([@pamil](https://github.com/pamil)) - [#212](https://github.com/Sylius/SyliusResourceBundle/issues/212) Normalise composer.json ([@pamil](https://github.com/pamil)) - [#213](https://github.com/Sylius/SyliusResourceBundle/issues/213) Update component's composer.json and normalise it ([@pamil](https://github.com/pamil)) - [#214](https://github.com/Sylius/SyliusResourceBundle/issues/214) Do not use deprecated Doctrine Inflector API ([@pamil](https://github.com/pamil)) - [#215](https://github.com/Sylius/SyliusResourceBundle/issues/215) Fix InMemoryRepository::applyOrder implementation ([@pamil](https://github.com/pamil)) - [#216](https://github.com/Sylius/SyliusResourceBundle/issues/216) [CI] Better job naming ([@pamil](https://github.com/pamil)) - [#220](https://github.com/Sylius/SyliusResourceBundle/issues/220) Duplicate initialisation of repositories ([@Fantus](https://github.com/Fantus)) - [#224](https://github.com/Sylius/SyliusResourceBundle/issues/224) Require previously required dependencies ([@pamil](https://github.com/pamil)) - [#225](https://github.com/Sylius/SyliusResourceBundle/issues/225) Bump up dev dependencies ([@pamil](https://github.com/pamil)) - [#226](https://github.com/Sylius/SyliusResourceBundle/issues/226) Remove Gedmo/DoctrineExtensions from dependencies ([@pamil](https://github.com/pamil)) - [#227](https://github.com/Sylius/SyliusResourceBundle/issues/227) Apply misc static analysis fixes ([@pamil](https://github.com/pamil)) - [#228](https://github.com/Sylius/SyliusResourceBundle/issues/228) Add an ability to define your own Inflector for Metadata class ([@pamil](https://github.com/pamil)) - [#229](https://github.com/Sylius/SyliusResourceBundle/issues/229) Fix tests namespace in the bundle ([@pamil](https://github.com/pamil)) - [#230](https://github.com/Sylius/SyliusResourceBundle/issues/230) Do not rely on services in DoctrineORMDriver ([@pamil](https://github.com/pamil)) - [#231](https://github.com/Sylius/SyliusResourceBundle/issues/231) Remove unnecessary BC layer for symfony/dependency-injection <=4.2 ([@pamil](https://github.com/pamil)) - [#232](https://github.com/Sylius/SyliusResourceBundle/issues/232) Create ResourceBundle's EntityRepository only if custom repository is not set ([@pamil](https://github.com/pamil)) - [#234](https://github.com/Sylius/SyliusResourceBundle/issues/234) Refactor test app ([@loic425](https://github.com/loic425)) - [#238](https://github.com/Sylius/SyliusResourceBundle/issues/238) Always set sylius.doctrine.orm.container_repository_factory.entities parameter ([@pamil](https://github.com/pamil)) - [#239](https://github.com/Sylius/SyliusResourceBundle/issues/239) Fix the static analysis build ([@pamil](https://github.com/pamil)) ### v1.7.0-RC.4 (2020-12-07) #### Details - [#234](https://github.com/Sylius/SyliusResourceBundle/issues/234) Refactor test app ([@loic425](https://github.com/loic425)) - [#238](https://github.com/Sylius/SyliusResourceBundle/issues/238) Always set sylius.doctrine.orm.container_repository_factory.entities parameter ([@pamil](https://github.com/pamil)) - [#239](https://github.com/Sylius/SyliusResourceBundle/issues/239) Fix the static analysis build ([@pamil](https://github.com/pamil)) ### v1.7.0-RC.3 (2020-11-25) #### Details - [#232](https://github.com/Sylius/SyliusResourceBundle/issues/232) Create ResourceBundle's EntityRepository only if custom repository is not set ([@pamil](https://github.com/pamil)) ### v1.7.0-RC.2 (2020-11-25) #### Details - [#230](https://github.com/Sylius/SyliusResourceBundle/issues/230) Do not rely on services in DoctrineORMDriver ([@pamil](https://github.com/pamil)) - [#231](https://github.com/Sylius/SyliusResourceBundle/issues/231) Remove unnecessary BC layer for symfony/dependency-injection <=4.2 ([@pamil](https://github.com/pamil)) ### v1.7.0-RC.1 (2020-11-24) #### TL;DR - Added an ability to customise the inflector used by Metadata class - All the packages made optional in previous 1.7.0-BETA releases were made required once again #### Details - [#224](https://github.com/Sylius/SyliusResourceBundle/issues/224) Require previously required dependencies ([@pamil](https://github.com/pamil)) - [#225](https://github.com/Sylius/SyliusResourceBundle/issues/225) Bump up dev dependencies ([@pamil](https://github.com/pamil)) - [#226](https://github.com/Sylius/SyliusResourceBundle/issues/226) Remove Gedmo/DoctrineExtensions from dependencies ([@pamil](https://github.com/pamil)) - [#227](https://github.com/Sylius/SyliusResourceBundle/issues/227) Apply misc static analysis fixes ([@pamil](https://github.com/pamil)) - [#228](https://github.com/Sylius/SyliusResourceBundle/issues/228) Add an ability to define your own Inflector for Metadata class ([@pamil](https://github.com/pamil)) - [#229](https://github.com/Sylius/SyliusResourceBundle/issues/229) Fix tests namespace in the bundle ([@pamil](https://github.com/pamil)) ### v1.7.0-BETA.5 (2020-11-23) #### TL;DR - Modernized the usage of Doctrine Inflector not to use deprecated API - Made sure there is only one instance of repository service for each resource #### Details - [#214](https://github.com/Sylius/SyliusResourceBundle/issues/214) Do not use deprecated Doctrine Inflector API ([@pamil](https://github.com/pamil)) - [#215](https://github.com/Sylius/SyliusResourceBundle/issues/215) Fix InMemoryRepository::applyOrder implementation ([@pamil](https://github.com/pamil)) - [#216](https://github.com/Sylius/SyliusResourceBundle/issues/216) [CI] Better job naming ([@pamil](https://github.com/pamil)) - [#220](https://github.com/Sylius/SyliusResourceBundle/issues/220) Duplicate initialisation of repositories ([@Justus Krapp](https://github.com/Fantus)) - [#221](https://github.com/Sylius/SyliusResourceBundle/issues/221) Update vimeo/psalm requirement from 4.1.1 to 4.2.1 ([@dependabot-preview](https://github.com/dependabot-preview)) - [#223](https://github.com/Sylius/SyliusResourceBundle/issues/223) Update phpstan/phpstan requirement from 0.12.49 to 0.12.57 ([@dependabot-preview](https://github.com/dependabot-preview)) ### v1.7.0-BETA.4 (2020-11-05) #### TL;DR - Made `winzou/state-machine-bundle` optional - Made `symfony/twig-bundle` optional #### Details - [#202](https://github.com/Sylius/SyliusResourceBundle/issues/202) Remove winzou state machine dependency ([@loic425](https://github.com/loic425)) - [#206](https://github.com/Sylius/SyliusResourceBundle/issues/206) Remove twig bundle dependency ([@loic425](https://github.com/loic425)) - [#207](https://github.com/Sylius/SyliusResourceBundle/issues/207) Bump doctrine/persistance version ([@dotdevru](https://github.com/dotdevru)) - [#209](https://github.com/Sylius/SyliusResourceBundle/issues/209) [Travis] Use symfony/flex ^1.10 instead of dev-master ([@pamil](https://github.com/pamil)) - [#211](https://github.com/Sylius/SyliusResourceBundle/issues/211) Upgrade to Psalm 4 ([@pamil](https://github.com/pamil)) - [#212](https://github.com/Sylius/SyliusResourceBundle/issues/212) Normalise composer.json ([@pamil](https://github.com/pamil)) - [#213](https://github.com/Sylius/SyliusResourceBundle/issues/213) Update component's composer.json and normalise it ([@pamil](https://github.com/pamil)) ### v1.7.0-BETA.3 (2020-10-15) #### Details - [#198](https://github.com/Sylius/SyliusResourceBundle/issues/198) Bump up minimal requirements to Symfony ^5.1 ([@pamil](https://github.com/pamil)) - [#199](https://github.com/Sylius/SyliusResourceBundle/issues/199) Update the year in LICENSE file ([@ValentineJester](https://github.com/ValentineJester)) - [#200](https://github.com/Sylius/SyliusResourceBundle/issues/200) Use HTTPS instead of HTTP in links in composer.json ([@ValentineJester](https://github.com/ValentineJester)) - [#201](https://github.com/Sylius/SyliusResourceBundle/issues/201) Remove outdated persistence backends from the documentation ([@ValentineJester](https://github.com/ValentineJester)) - [#204](https://github.com/Sylius/SyliusResourceBundle/issues/204) Replace AbstractController with ControllerTrait & ContainerAwareInterface ([@pamil](https://github.com/pamil)) ### v1.7.0-BETA.2 (2020-10-14) #### TL;DR - Made FOSRestBundle optional - Ensured WinzouStateMachineBundle services and aliases are public #### Details - [#173](https://github.com/Sylius/SyliusResourceBundle/issues/173) Remove rest dependencies ([@loic425](https://github.com/loic425), [@pamil](https://github.com/pamil)) - [#177](https://github.com/Sylius/SyliusResourceBundle/issues/177) Pagerfanta updates ([@mbabker](https://github.com/mbabker)) - [#178](https://github.com/Sylius/SyliusResourceBundle/issues/178) When using winzouStateMachineBundle 0.4, the old named services are aliases, so need to be marked public as well ([@mbabker](https://github.com/mbabker), [@pamil](https://github.com/pamil)) - [#192](https://github.com/Sylius/SyliusResourceBundle/issues/192) Remove conflict with amphp/amp ([@pamil](https://github.com/pamil)) - [#195](https://github.com/Sylius/SyliusResourceBundle/issues/195) Upgrade to Psalm v3.17.1 ([@pamil](https://github.com/pamil)) - [#196](https://github.com/Sylius/SyliusResourceBundle/issues/196) Remove unnecessary dev dependency on "polishsymfonycommunity/symfony-mocker-container" ([@pamil](https://github.com/pamil)) - [#197](https://github.com/Sylius/SyliusResourceBundle/issues/197) Make RegisterFormBuilderPass accept multiple tags on a single service ([@pamil](https://github.com/pamil)) ### v1.7.0-BETA.1 (2020-10-13) #### TL;DR - Added support for Symfony 5 - Added support for Twig 3 - Added support for `winzou/state-machine-bundle` 0.4 and 0.5 #### Details - [#114](https://github.com/Sylius/SyliusResourceBundle/issues/114) Updating composer dependencies ([@mamazu](https://github.com/mamazu)) - [#117](https://github.com/Sylius/SyliusResourceBundle/issues/117) Fix extended types deprecation ([@dannyvw](https://github.com/dannyvw)) - [#119](https://github.com/Sylius/SyliusResourceBundle/issues/119) Update composer dependencies ([@dannyvw](https://github.com/dannyvw)) - [#124](https://github.com/Sylius/SyliusResourceBundle/issues/124) Replace deprecated doctrine object manager ([@loic425](https://github.com/loic425)) - [#125](https://github.com/Sylius/SyliusResourceBundle/issues/125) Replace deprecated doctrine object repository ([@loic425](https://github.com/loic425)) - [#126](https://github.com/Sylius/SyliusResourceBundle/issues/126) Replace dbal types ([@loic425](https://github.com/loic425)) - [#127](https://github.com/Sylius/SyliusResourceBundle/issues/127) Fix phpspec tests on DefaultFormBuilder ([@loic425](https://github.com/loic425)) - [#128](https://github.com/Sylius/SyliusResourceBundle/issues/128) Lock static analysis tools versions & fix the build ([@pamil](https://github.com/pamil)) - [#129](https://github.com/Sylius/SyliusResourceBundle/issues/129) Add support for PHP 7.4 and Symfony 4.4 ([@dannyvw](https://github.com/dannyvw), [@pamil](https://github.com/pamil)) - [#130](https://github.com/Sylius/SyliusResourceBundle/issues/130) Remove deprecated templating configuration ([@dannyvw](https://github.com/dannyvw)) - [#131](https://github.com/Sylius/SyliusResourceBundle/issues/131) Allow twig 3.x ([@dannyvw](https://github.com/dannyvw)) - [#132](https://github.com/Sylius/SyliusResourceBundle/issues/132) Fix controller deprecation ([@dannyvw](https://github.com/dannyvw)) - [#133](https://github.com/Sylius/SyliusResourceBundle/issues/133) Fix testing multiple Symfony versions, add build for 5.0 & remove support for <4.4 ([@pamil](https://github.com/pamil)) - [#135](https://github.com/Sylius/SyliusResourceBundle/issues/135) Allow for DoctrineBundle ^2.0 ([@pamil](https://github.com/pamil)) - [#136](https://github.com/Sylius/SyliusResourceBundle/issues/136) Remove unneccessary dependency on winzou/state-machine in the component ([@pamil](https://github.com/pamil)) - [#138](https://github.com/Sylius/SyliusResourceBundle/issues/138) Remove legacy di configuration ([@dannyvw](https://github.com/dannyvw)) - [#139](https://github.com/Sylius/SyliusResourceBundle/issues/139) Fix event dispatcher deprecation ([@dannyvw](https://github.com/dannyvw)) - [#141](https://github.com/Sylius/SyliusResourceBundle/issues/141) Upgrade to PHPStan 0.12 ([@GSadee](https://github.com/GSadee)) - [#142](https://github.com/Sylius/SyliusResourceBundle/issues/142) Github repository configuration from Sylius/Sylius ([@CoderMaggie](https://github.com/CoderMaggie)) - [#143](https://github.com/Sylius/SyliusResourceBundle/issues/143) [Maintenance] Updated branch alias ([@lchrusciel](https://github.com/lchrusciel)) - [#144](https://github.com/Sylius/SyliusResourceBundle/issues/144) [Maintenance] Update github templates ([@lchrusciel](https://github.com/lchrusciel)) - [#151](https://github.com/Sylius/SyliusResourceBundle/issues/151) [Maintenance] Bump ApiTestCase to v5.0 ([@lchrusciel](https://github.com/lchrusciel)) - [#159](https://github.com/Sylius/SyliusResourceBundle/issues/159) Remove duplicated docblocks ([@GSadee](https://github.com/GSadee)) - [#160](https://github.com/Sylius/SyliusResourceBundle/issues/160) ResourceBundle documentation extracted to its repository () - [#161](https://github.com/Sylius/SyliusResourceBundle/issues/161) [HOTFIX] Conflict with amphp/amp 2.4.3 ([@lchrusciel](https://github.com/lchrusciel)) - [#162](https://github.com/Sylius/SyliusResourceBundle/issues/162) [Documentation] Fix index menu ([@SirDomin](https://github.com/SirDomin)) - [#163](https://github.com/Sylius/SyliusResourceBundle/issues/163) [HOTFIX] Conflict with the newest amphp/amp ([@lchrusciel](https://github.com/lchrusciel)) - [#165](https://github.com/Sylius/SyliusResourceBundle/issues/165) Fix the build ([@pamil](https://github.com/pamil)) - [#167](https://github.com/Sylius/SyliusResourceBundle/issues/167) Upgrade rest bundle ([@loic425](https://github.com/loic425)) - [#168](https://github.com/Sylius/SyliusResourceBundle/issues/168) [Docs] Serialization groups of the elements in a paginated collection ([@vvasiloi](https://github.com/vvasiloi)) - [#172](https://github.com/Sylius/SyliusResourceBundle/issues/172) Check if form is submitted on resource creation/edition ([@loic425](https://github.com/loic425)) - [#175](https://github.com/Sylius/SyliusResourceBundle/issues/175) Upgrade PagerfantaBundle to new version with B/C layer ([@mbabker](https://github.com/mbabker)) - [#181](https://github.com/Sylius/SyliusResourceBundle/issues/181) Fix build ([@loic425](https://github.com/loic425)) - [#182](https://github.com/Sylius/SyliusResourceBundle/issues/182) Testing several state machine versions ([@loic425](https://github.com/loic425)) - [#187](https://github.com/Sylius/SyliusResourceBundle/issues/187) Symfony 5 support ([@loic425](https://github.com/loic425)) - [#189](https://github.com/Sylius/SyliusResourceBundle/issues/189) Require webmozart/assert as it's used by the bundle code ([@pamil](https://github.com/pamil)) - [#190](https://github.com/Sylius/SyliusResourceBundle/issues/190) Fix errors reported by static analysis tools ([@pamil](https://github.com/pamil)) - [#191](https://github.com/Sylius/SyliusResourceBundle/issues/191) Normalise composer.json (with ergebnis/composer-normalize) ([@pamil](https://github.com/pamil)) - [#193](https://github.com/Sylius/SyliusResourceBundle/issues/193) Bump up requirements to PHP ^7.3 ([@pamil](https://github.com/pamil)) - [#194](https://github.com/Sylius/SyliusResourceBundle/issues/194) Fix deprecations and errors while running PHPUnit ([@pamil](https://github.com/pamil)) ## CHANGELOG FOR `1.6.x` ### v1.6.4 (2020-08-18) Security release: - [CVE-2020-15143: Remote Code Execution in ParametersParser while using request parameters inside expression language](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-p4pj-9g59-4ppv) - [CVE-2020-15146: Remote Code Execution in OptionsParser while using request parameters inside expression language](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-h6m7-j4h3-9rf5) ### v1.6.3 (2020-01-27) Security release: - [CVE-2020-5220: Ability to define unintended serialisation groups via HTTP header which might lead to data exposure](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-8vp7-j5cj-vvm2) ### v1.6.2 (2020-01-13) - [#145](https://github.com/Sylius/SyliusResourceBundle/issues/145) Autowire Doctrine\Persistence\ObjectManager ([@pamil](https://github.com/pamil)) ### v1.6.1 (2019-10-10) - [#112](https://github.com/Sylius/SyliusResourceBundle/issues/112) Support for Symfony 3.4 / 4.3+ ([@pamil](https://github.com/pamil)) ### v1.6.0 (2019-10-07) - [#104](https://github.com/Sylius/SyliusResourceBundle/issues/104) [RFC] Add success flashes before post event ([@Zales0123](https://github.com/Zales0123)) - [#110](https://github.com/Sylius/SyliusResourceBundle/issues/110) Add Psalm and fix all the errors ([@pamil](https://github.com/pamil)) ### v1.6.0-RC.3 (2019-06-18) - [#92](https://github.com/Sylius/SyliusResourceBundle/issues/92) Removing '2' from 'Symfony2' ([@loevgaard](https://github.com/loevgaard)) - [#95](https://github.com/Sylius/SyliusResourceBundle/issues/95) Autowire factories and repositories by class and name ([@pamil](https://github.com/pamil)) - [#97](https://github.com/Sylius/SyliusResourceBundle/issues/97) Autodiscover resource's model interfaces and deprecate explicit configuration ([@pamil](https://github.com/pamil)) ### v1.6.0-RC.2 (2019-06-07) - [#91](https://github.com/Sylius/SyliusResourceBundle/issues/91) Support for Gedmo/DoctrineExtensions ([@pamil](https://github.com/pamil)) ### v1.6.0-RC.1 (2019-06-07) - [#88](https://github.com/Sylius/SyliusResourceBundle/issues/88) Ensure forward compatibility with ResolveTargetEntityListener ([@teohhanhui](https://github.com/teohhanhui)) - [#89](https://github.com/Sylius/SyliusResourceBundle/issues/89) Add support for embeddables ([@pamil](https://github.com/pamil)) - [#90](https://github.com/Sylius/SyliusResourceBundle/issues/90) Drop Symfony 4.1, add support for Symfony 4.3 ([@pamil](https://github.com/pamil)) ## CHANGELOG FOR `1.5.x` ### v1.5.2 (2020-08-18) Security release: - [CVE-2020-15143: Remote Code Execution in ParametersParser while using request parameters inside expression language](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-p4pj-9g59-4ppv) - [CVE-2020-15146: Remote Code Execution in OptionsParser while using request parameters inside expression language](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-h6m7-j4h3-9rf5) ### v1.5.1 (2020-01-27) Security release: - [CVE-2020-5220: Ability to define unintended serialisation groups via HTTP header which might lead to data exposure](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-8vp7-j5cj-vvm2) ### v1.5.0 (2019-05-07) #### TL;DR Released ResourceBundle as a standalone package, containing a subtree split of Resource component. ## CHANGELOG FOR `1.4.x` ### v1.4.7 (2020-08-18) Security release: - [CVE-2020-15143: Remote Code Execution in ParametersParser while using request parameters inside expression language](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-p4pj-9g59-4ppv) - [CVE-2020-15146: Remote Code Execution in OptionsParser while using request parameters inside expression language](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-h6m7-j4h3-9rf5) ### v1.4.6 (2020-01-27) Security release: - [CVE-2020-5220: Ability to define unintended serialisation groups via HTTP header which might lead to data exposure](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-8vp7-j5cj-vvm2) ### v1.4.5 (2019-10-07) - [#88](https://github.com/Sylius/SyliusResourceBundle/issues/88) Ensure forward compatibility with ResolveTargetEntityListener ([@teohhanhui](https://github.com/teohhanhui)) - [#104](https://github.com/Sylius/SyliusResourceBundle/issues/104) [RFC] Add success flashes before post event ([@Zales0123](https://github.com/Zales0123)) ### v1.4.4 (2019-05-07) #### TL;DR Released ResourceBundle as a standalone package, containing a subtree split of Resource component. ## CHANGELOG FOR `1.3.x` ### v1.3.14 (2020-08-18) Security release: - [CVE-2020-15143: Remote Code Execution in ParametersParser while using request parameters inside expression language](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-p4pj-9g59-4ppv) - [CVE-2020-15146: Remote Code Execution in OptionsParser while using request parameters inside expression language](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-h6m7-j4h3-9rf5) ### v1.3.13 (2020-01-27) Security release: - [CVE-2020-5220: Ability to define unintended serialisation groups via HTTP header which might lead to data exposure](https://github.com/Sylius/SyliusResourceBundle/security/advisories/GHSA-8vp7-j5cj-vvm2) ### v1.3.12 (2019-05-07) #### TL;DR Released ResourceBundle as a standalone package, containing a subtree split of Resource component. ## CHANGELOG FOR `1.2.x` ### v1.2.17 (2019-05-07) #### TL;DR Released ResourceBundle as a standalone package, containing a subtree split of Resource component. ## CHANGELOG FOR `1.1.x` ### v1.1.18 (2019-05-07) #### TL;DR Released ResourceBundle as a standalone package, containing a subtree split of Resource component. ================================================ FILE: CONFLICTS.md ================================================ # CONFLICTS This document explains why certain conflicts were added to `composer.json` and references related issues. - `willdurand/hateoas-bundle: ^2.6` This version allows Symfony 7 but does not support the "annotation_reader" service removal. @see https://github.com/willdurand/BazingaHateoasBundle/issues/108 ================================================ FILE: Dockerfile ================================================ ARG COMPOSER_VERSION=2.3 ARG PHP_VERSION=8.4 FROM composer:${COMPOSER_VERSION} AS composer FROM mlocati/php-extension-installer AS php_extension_installer FROM php:${PHP_VERSION}-cli-alpine AS php COPY --from=composer /usr/bin/composer /usr/bin/composer COPY --from=php_extension_installer /usr/bin/install-php-extensions /usr/bin/install-php-extensions RUN install-php-extensions pdo_sqlite COPY . /app WORKDIR /app RUN echo "memory_limit=512M" > /usr/local/etc/php/conf.d/memory-limit.ini RUN composer global config --no-plugins allow-plugins.symfony/flex true RUN composer global require --no-progress --no-scripts --no-plugins "symfony/flex:^1.10" RUN composer update --with-all-dependencies --no-interaction --no-progress WORKDIR /app/tests/Application RUN php bin/console doctrine:schema:create WORKDIR /app ================================================ FILE: LICENSE ================================================ Copyright (c) 2011-present Sylius Sp. z o.o. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: LICENSE_OF_TRADEMARK_AND_LOGO ================================================ Encourage widespread and fair use of Sylius logo and brand identity. This Trademarks and Logos use policy (the “Policy”) is based on the Ubuntu and Symfony trademark policy and published under the CC-BY-SA license. You are welcome to base your own project trademark policies off it, just let others use your changes and give credit to the Ubuntu and Symfony projects as the original source! Version n°1. Published on April 5th 2019. The objective of the Policy is to encourage widespread use of the Sylius trademarks by the Sylius community while controlling that use in order to avoid confusion on the part of Sylius users and the general public, to maintain the value of the image and reputation of the trademarks and to protect them from inappropriate or unauthorised use. The sections below describe what is allowed, what isn’t allowed, and cases in which you should ask permission. If you have any doubt, please contact us and a member of our legal representative will be in touch with you shortly. If you are aware a breach or misuse of the Sylius trademarks in any way, we would appreciate you bringing this to our attention. Please contact us so that we can investigate this further. The Trademarks and Logos Sylius sp. z o.o. owns the verbal trademark containing in whole or part of the word “Sylius”. Any verbal mark starting with the letters “Sylius” is sufficiently similar to one or more of the trademarks that permission will be needed in order to use it. All verbal trademarks of Sylius sp. z o.o., all distinctive signs used in commerce by Sylius sp. z o.o. to designate his products or services related to Sylius are collectively referred to as the “Trademarks”. Permitted use of the Trademarks Certain usages of the Trademarks are fine and no specific permission from us is needed. Community advocacy. Sylius is built by its community. We share access to the Trademarks with the entire community for the purposes of discussion, development and advocacy. We recognise that most of the open source discussion and development areas are for non-commercial purposes and will allow the use of the Trademarks in this context, provided: the Trademark is used in a manner consistent with this Policy; there is no commercial intent behind the use; what you are referring to is in fact Sylius. If someone is confused into thinking that what isn’t Sylius is, in fact, Sylius, you are probably doing something wrong; there is no suggestion (through words or appearance) that your project is approved, sponsored, or affiliated with Sylius, Sylius sp. z o.o. or its related projects unless it actually has been approved by and is accountable to Sylius sp. z o.o. and the Sylius Project. Building on Sylius or for Sylius. If you are producing new software which is intended for use with or on Sylius, you may use the Trademark in a way which indicates the intent of your product. For example, if you are developing a system management tool for Sylius, acceptable project titles would be “System Management for Sylius” or “Sylius Based Systems Management”. We would strongly discourage, and likely would consider to be problematic, a name such as SyliusMan, Sylius Management, etc. Furthermore, you may not use the Trademarks in a way which implies an endorsement where that doesn’t exist, or which attempts to unfairly or confusingly capitalise on the goodwill or brand of the project. Commentary and parody. The Trademarks and Logos are designed to cover use of a mark to imply origin or endorsement by the project. When a user downloads something called Sylius, they should know it comes from the Sylius project. This helps Sylius build a reputation that will not be damaged by confusion around what is, and isn’t, Sylius. Using the Trademarks in your discussion, commentary, criticism or parody, in ways that unequivocally do not imply endorsement, is permissible. Anyone is free to write articles, create websites, blog about, or talk about Sylius — as long as it’s clear to everyone — including people completely unfamiliar with Sylius — that they are simply referring to Sylius and in no way speaking for the Sylius project and/or for Sylius sp. z o.o. We reserve the right to review all usage within the open source community, and to object to any usage that appears to overstep the bounds of discussion and good-faith non-commercial development. In any event, once a project has left the open source project phase or otherwise become a commercial project, this Policy does not authorise any use of the Trademarks in connection to that project. Restricted use that requires a trademark licence Permission from us is necessary to use any of the Trademarks under any circumstances other than those specifically permitted above. These include but are not limited to: Any commercial use including for any services related to Sylius such as providing training services, conference services, or design services (should you wish to provide such services, please contact us beforehand to explore Sylius Solution Partner Program); Use on or in relation to a software product that includes or is built on top of a product supplied by us, if there is any commercial intent associated with that product; Use in a domain name or URL; Use for merchandising purposes, e.g. on t-shirts and the like. If you wish to have permission for any of the uses above or for any other use which is not specifically referred to in this Policy, please contact us and we’ll let you know as soon as possible if your proposed use is permissible. Permission may only be granted subject to certain conditions and these may include the requirement that you enter into an agreement with us to maintain the quality of the product and/or service which you intend to supply at a prescribed level. While there may be exceptions, it is very unlikely that we will approve Trademark use in the following cases: Use of a Trademark in a company name; Use of a Trademark in a domain name which has a commercial intent. The commercial intent can range from promotion of a company or product, to collecting revenue generated by advertising; The calling of any software or product by the name Sylius (or another related Trademark); Use in combination with any other marks or logos. This include use of a Trademark in a manner that creates a “combined mark,” or use that integrates other wording with the Trademark in a way that the public may think of the use as a new mark (for example Club Sylius or SyliusBooks, or in a way that by use of special fonts or presentation with nearby words or images conveys an impression that the two are tied in some way); Use in combination with any product or service which is presented as being Certified or Official or formally associated with us or our products or services; Use in a way which implies an endorsement where that doesn’t exist, or which attempts to unfairly or confusingly capitalise on the goodwill or brand of the project; Use of a Trademark in a manner that disparages Sylius, or Sylius sp. z o.o.; or its products and is not clearly third-party parody; Use of a Trademark on or in relation to a software product which constitutes a substantially modified version of a product supplied by the Sylius project, that is to say with material changes to the code, or services relating to such a product; and Use of a Trademark in a title or metatag of a web page whose sole intention or result is to influence search engine rankings or result listings (for example use as keyword for advertising purposes), rather than for discussion, development or advocacy of the Trademarks. Logo usage guidelines Except otherwise agreed, any use of Logos shall be expressly authorized by writing by Sylius sp. z o.o.. To get any authorization to use any Logo, please contact us and a member of our team will be in touch with you shortly. Our logos are presented in multiple colours and it is important that their visual integrity be maintained. Therefore, when use of Logos is authorized, it is therefore preferable that the logos only be used in their standard form but if you should feel the need to alter them in any way you should keep the following guidelines in mind. It should also be borne in mind that the more you wish to vary our logos from their standard form the smaller is the chance that we will be able to approve your proposed use. If presented in multiple colours, the logo should only use the “official” logo colours. You may use transparency and gradient/depth tools but should retain the “official” colours. Any scaling must retain the original proportions of the logo. In case of non-compliance with Trademarks and Logos’ Use Policy or applicable law, any use of the Trademarks and/or Logos will be prohibited. ================================================ FILE: Makefile ================================================ PACKAGE := @docker compose run --rm package .PHONY: test: $(PACKAGE) composer test .PHONY: analyse: $(PACKAGE) composer analyse ================================================ FILE: README.md ================================================ SyliusResourceBundle ==================== Easy CRUD and persistence for Symfony apps. During our work on Sylius, we noticed a lot of duplicated code across all controllers. We started looking for good solution of the problem. We're not big fans of administration generators (they're cool, but not for our usecase!) - we wanted something simpler and more flexible. Another idea was to not limit ourselves to one persistence backend. Initial implementation included custom manager classes, which was quite of overhead, so we decided to simply stick with Doctrine Common Persistence interfaces. If you are using Doctrine ORM or any of the ODM's, you're already familiar with those concepts. Resource bundle relies mainly on `ObjectManager` and `ObjectRepository` interfaces. The last annoying problem this bundle is trying to solve, is having separate "backend" and "frontend" controllers, or any other duplication for displaying the same resource, with different presentation (view). We also wanted an easy way to filter some resources from list, sort them or display by id, slug or any other criteria - without having to defining another super simple action for that purpose. If these are issues you're struggling with, this bundle may be helpful! Please note that this bundle **is not an admin generator**. It won't create forms, filters and grids for you. It only provides format agnostic controllers as a foundation to build on, with some basic sorting and filter mechanisms. ### Supported branches - `1.10` (`v1.10.*` versions) - bug fixes and improvements of existing features - `1.11` (next version - `v1.11.0`) - new features and bigger changes Beware! There is no `main` or `master` branch on the repository. You should always open a Pull Request to the branch named as the minor version on which your changes should be applied. Sylius ------

Sylius Logo.

Sylius is an Open Source eCommerce solution built from decoupled components with powerful API and the highest quality code. [Read more on sylius.com](http://sylius.com). Development ----------- #### Build: ```bash docker compose up -d --build ``` #### Test: ```bash make test ``` Documentation ------------- [Sylius Stack documentation » Resource Bundle](https://stack.sylius.com/resource/index) Contributing ------------ [This page](http://docs.sylius.com/en/latest/contributing/index.html) contains all the information about contributing to Sylius. Follow Sylius' Development -------------------------- If you want to keep up with the updates and latest features, follow us on the following channels: * [Official Blog](https://sylius.com/blog) * [Sylius on Twitter](https://twitter.com/Sylius) * [Sylius on Facebook](https://facebook.com/SyliusEcommerce) Bug tracking ------------ Sylius uses [GitHub issues](https://github.com/Sylius/SyliusResourceBundle/issues). If you have found bug, please create an issue. MIT License ----------- License can be found [here](https://github.com/Sylius/SyliusResourceBundle/blob/1.10/LICENSE). Authors ------- The bundle was originally created by [Paweł Jędrzejewski](http://pjedrzejewski.com). See the list of [contributors](https://github.com/Sylius/SyliusResourceBundle/graphs/contributors). ================================================ FILE: UPGRADE.md ================================================ ## UPGRADE FOR `1.15.x` ### FROM `1.14.x` to `1.15.x` #### Routing Path Routing paths may have been changed depending on your configuration. *Using ResourceController* ```yaml # config/packages/ sylius_resource: routing_path_bc_layer: false ``` If you have disabled the routing path bc-layer, the bulk delete operations will have this path change: example: ```diff -/science-books/bulk_delete +/science-books/bulk-delete ``` *Using the new routing system with `AsResource` attribute and operations* If the shortName of your operation contains an underscore, it will be replaced by a dash. example: ```diff -/subscriptions/bulk_delete +/subscriptions/bulk-delete -/subscriptions/bulk_publish +/subscriptions/bulk-publish ``` Note this will keep an underscore if you have configured the resource bundle like this: ```yaml # config/packages/ sylius_resource: path_segment_name_generator: sylius.metadata.path_segment_name_generator.underscore ``` Please note this configuration is not recommended for SEO. ## UPGRADE FOR `1.14.x` ### FROM `1.13.x` to `1.14.x` #### Minimal Requirements The minimum required PHP version is now 8.2. #### Routing Path Routing paths may have been changed depending on your configuration. | behat/transliterator | Routing Path BC-layer | Routing paths | |----------------------|-----------------------|---------------| | ✅ Installed | ✅ Enabled by default | ✅ Unchanged | | ❌ Not installed | ❌ Disabled by default | ❌ Changed | If you want to keep the previous paths, please follow these following steps: 1. Make sure to have Behat transliterator installed. ```shell composer require behat/transliterator ``` 2/ Ensure routing path bc-layer is enabled ```yaml # config/packages/ sylius_resource: routing_path_bc_layer: true ``` When using the `ResourceController` for your routes and when the BC-layer is enabled, the routing path contains a trailing slash by default. In 2.x, it will be removed. You can disable the BC-layer to remove these trailing slashes on all your routes and be prepared for the 2.x release. If you are using the new Routing system with `AsResource` attribute and operations, this change has no effect, there is no trailing slash in both configuration. ```yaml # config/packages/ sylius_resource: routing_path_bc_layer: false ``` | Routing system | Routing Path BC-layer | Trailing slash | |------------------------------|-----------------------|----------------| | Using the ResourceController | ✅ Enabled | ✅ | | Using the ResourceController | ❌ Disabled | ❌ | | Using AsResource attribute | ✅ Enabled | ❌ | | Using AsResource attribute | ❌ Disabled | ❌ | The routing path uses dashes to separate words by default. Eg: `/shipping-categories`. On the new routing system only, you can configure using underscores instead. Eg: `/shipping_categories` ```yaml # config/packages/ sylius_resource: path_segment_name_generator: sylius.metadata.path_segment_name_generator.underscore ``` Note that even if this is configurable, it's recommended to use dashes to separate the words for SEO. *Source:* * https://developers.google.com/search/docs/crawling-indexing/url-structure ## UPGRADE FOR `1.13.x` ### FROM `1.12.x` to `1.13.x` The `Sylius\Resource\Exception\VariantWithNoOptionsValuesException` and `Sylius\Component\Resource\Exception\VariantWithNoOptionsValuesException` classes have been deprecated and will be removed in `2.0`. ## UPGRADE FOR `1.12.x` ### FROM `1.11.x` to `1.12.x` In preparation of removal, following dependencies were moved to optional requirements. If still in use by your app, require them explicitly in your `composer.json`. * friendsofsymfony/rest-bundle * jms/serializer-bundle * willdurand/hateoas-bundle * winzou/state-machine-bundle ## UPGRADE FOR `1.11.x` ### FROM `1.11.0` to `1.11.1` The `Sylius\Bundle\ResourceBundle\EventListener\ORMTranslatableListener` service has become a Doctrine subscriber again to fully support Symfony 5. ### FROM `1.10.x` to `1.11.x` We remove the default mapping paths which are used to read PHP 8 attributes to build routes. To configure this default value, please edit your `config/packages/sylius_resource.yaml` file to add your mapping paths manually: ```yaml # config/packages/sylius_resource.yaml sylius_resource: mapping: paths: - '%kernel.project_dir%/src/Entity' ``` These following services are now Doctrine listeners instead of Doctrine subscribers ( @see https://github.com/symfony/symfony/issues/49586) * Sylius\Bundle\ResourceBundle\EventListener\ORMTranslatableListener * Sylius\Bundle\ResourceBundle\EventListener\ORMRepositoryClassSubscriber * Sylius\Bundle\ResourceBundle\EventListener\ORMMappedSuperClassSubscriber Applied the `UniqueEntity` constraint for `locale` and `translatable` fields, and added `NotBlank` & `Locale` constraints for the `locale` property in classes extending `Sylius\Resource\Model\AbstractTranslation`. Applied the `Valid` constraint for the `getTranslations` method for objects implementing `Sylius\Resource\Model\TranslatableInterface`. ## UPGRADE FOR `1.10.x` ### FROM `1.9.x` to `1.10.x` - failed form response status code returned from the `ResourceController::createAction` and `ResourceController::updateAction` changed from `200` to `422` see: https://github.com/Sylius/SyliusResourceBundle/pull/488. This is technically a bug fix, but could break the application if your logic is based on this bugged previous status code ## UPGRADE FOR `1.7.x` ### FROM `1.6.x` TO `1.7.x` #### Dependencies - `jms/serializer` and `jms/serializer-bundle` from `2.x` to `3.x`: follow their upgrade process for [the component](https://github.com/schmittjoh/serializer/blob/master/UPGRADING.md#from-2x-to-300) and [the bundle](https://github.com/schmittjoh/JMSSerializerBundle/blob/master/UPGRADING.md#upgrading-from-2x-to-30); if you're getting errors about serializing the entity manager, consider changing the serialized type from `array` to `iterable` for Doctrine collections in your models - `friendsofsymfony/rest-bundle` from `2.x` to `3.x`: follow their [upgrade process](https://github.com/FriendsOfSymfony/FOSRestBundle/blob/3.x/UPGRADING-3.0.md); if you're using this bundle to render HTML templates as well, replace it with direct calls to Twig and handle only the REST logic via this bundle ([see this PR for more](https://github.com/Sylius/SyliusResourceBundle/pull/167/files)) - `willdurand/hateoas` from `2.x` to `3.x`: follow their [upgrade process](https://github.com/willdurand/Hateoas/blob/master/UPGRADING.md#from-2120-to-300) - from `white-october/pagerfanta-bundle` `^1.0` to `babdev/pagerfanta-bundle` `^2.5`: follow their [upgrade process](https://github.com/BabDev/PagerfantaBundle/blob/2.x/UPGRADE-2.0.md#migrate-from-whiteoctoberpagerfantabundle-1x-to-babdevpagerfantabundle-20) - `doctrine/persistence` from `1.x` to `2.x` (if applicable): replace `Doctrine\Common\Persistence` with `Doctrine\Persistence` in your codebase #### Example You can find an exemplary upgrade to this version of ResourceBundle on Sylius repository in [this PR](https://github.com/Sylius/Sylius/pull/12084). ## UPGRADE FOR `1.3.x` ### FROM `1.3.x` TO `1.3.13` If you're using an "Accept" HTTP header to set the serialization groups, you need to define allowed groups either by passing them as default in `serialization_groups` setting or marking them as allowed in `allowed_serialization_groups` setting, both settings are set in the route definition (under `_sylius` key). ================================================ FILE: composer.json ================================================ { "name": "sylius/resource-bundle", "type": "symfony-bundle", "description": "Resource component for Sylius.", "keywords": [ "resource", "storage", "persistence", "sylius" ], "homepage": "https://sylius.com", "license": "MIT", "authors": [ { "name": "Paweł Jędrzejewski", "homepage": "https://pjedrzejewski.com" }, { "name": "Sylius project", "homepage": "https://sylius.com" }, { "name": "Community contributions", "homepage": "https://github.com/Sylius/Sylius/contributors" } ], "require": { "php": "^8.2", "babdev/pagerfanta-bundle": "^4.4", "doctrine/collections": "^2.2", "doctrine/event-manager": "^1.1 || ^2.0", "doctrine/inflector": "^2.0", "doctrine/persistence": "^3.3 || ^4.0", "sylius/registry": "^1.2", "symfony/config": "^6.4 || ^7.4 || ^8.0", "symfony/deprecation-contracts": "^3.5", "symfony/expression-language": "^6.4 || ^7.4 || ^8.0", "symfony/form": "^6.4 || ^7.4 || ^8.0", "symfony/framework-bundle": "^6.4 || ^7.4 || ^8.0", "symfony/http-foundation": "^6.4 || ^7.4 || ^8.0", "symfony/intl": "^6.4 || ^7.4 || ^8.0", "symfony/routing": "^6.4 || ^7.4 || ^8.0", "symfony/security-core": "^6.4 || ^7.4 || ^8.0", "symfony/security-csrf": "^6.4 || ^7.4 || ^8.0", "symfony/string": "^6.4 || ^7.4 || ^8.0", "symfony/translation": "^6.4 || ^7.4 || ^8.0", "symfony/twig-bundle": "^6.4 || ^7.4 || ^8.0", "symfony/validator": "^6.4 || ^7.4 || ^8.0", "symfony/yaml": "^6.4 || ^7.4 || ^8.0", "webmozart/assert": "^1.11", "willdurand/negotiation": "^3.1" }, "replace": { "sylius/resource": "self.version" }, "require-dev": { "coduo/php-matcher": "^6.0", "doctrine/data-fixtures": "^2.0", "doctrine/doctrine-bundle": "^2.13 || ^3.0 || ^4.0", "doctrine/orm": "^2.18 || ^3.3", "jackalope/jackalope": "^2.0", "jackalope/jackalope-doctrine-dbal": "^2.0", "matthiasnoback/symfony-dependency-injection-test": "^6.1.0", "openlss/lib-array2xml": "^1.0", "pagerfanta/pagerfanta": "^4.4", "phpcr/phpcr": "^2.1", "phpstan/phpstan": "^1.12", "phpstan/phpstan-phpunit": "^1.4", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^10.0", "rector/rector": "^0.18.2", "sylius-labs/coding-standard": "^4.4", "sylius/grid-bundle": "^1.13 || ^1.15@alpha", "symfony/browser-kit": "^6.4 || ^7.4 || ^8.0", "symfony/console": "^6.4 || ^7.4 || ^8.0", "symfony/css-selector": "^6.4 || ^7.4 || ^8.0", "symfony/dependency-injection": "^6.4 || ^7.4 || ^8.0", "symfony/dotenv": "^6.4 || ^7.4 || ^8.0", "symfony/http-kernel": "^6.4 || ^7.4 || ^8.0", "symfony/messenger": "^6.4 || ^7.4 || ^8.0", "symfony/security-bundle": "^6.4 || ^7.4 || ^8.0", "symfony/serializer": "^6.4 || ^7.4 || ^8.0", "symfony/stopwatch": "^6.4 || ^7.4 || ^8.0", "symfony/uid": "^6.4 || ^7.4 || ^8.0", "twig/twig": "^3.14", "zenstruck/foundry": "^2.3" }, "conflict": { "behat/transliterator": "<1.2", "doctrine/orm": "<2.18", "doctrine/doctrine-bundle": "<2.0", "doctrine/phpcr-odm": "<2.1", "friendsofsymfony/rest-bundle": "<3.7", "gedmo/doctrine-extensions": "<3.17.1", "jms/serializer-bundle": "<5.5", "pagerfanta/pagerfanta" : "<4.4", "willdurand/hateoas-bundle": "<2.5 || ^3.0", "winzou/state-machine-bundle": "<0.6.2", "symfony/workflow": "<6.4 || >=7.0,<7.4", "twig/twig": "<3.0" }, "suggest": { "doctrine/orm": "^2.20", "sylius/locale": "^1.0" }, "config": { "allow-plugins": { "symfony/flex": true, "dealerdirect/phpcodesniffer-composer-installer": false }, "sort-packages": true }, "extra": { "symfony": { "require": "8.0.*" } }, "autoload": { "psr-4": { "Sylius\\Bundle\\ResourceBundle\\": "src/Bundle/", "Sylius\\Component\\Resource\\": "src/Component/legacy/src/", "Sylius\\Resource\\": "src/Component/src/" } }, "autoload-dev": { "psr-4": { "Sylius\\Bundle\\ResourceBundle\\Tests\\": "tests/Bundle/", "Sylius\\Component\\Resource\\spec\\": "src/Component/legacy/spec/", "Sylius\\Component\\Resource\\Tests\\": "src/Component/legacy/tests/", "Sylius\\Resource\\Tests\\": "src/Component/tests/", "App\\": "tests/Application/src/", "Tests\\": "tests/" } }, "scripts": { "analyse": [ "@composer validate --strict", "vendor/bin/ecs check", "vendor/bin/phpstan analyse --ansi --memory-limit=256M -c phpstan.neon src" ], "fix": [ "vendor/bin/ecs check --fix" ], "test": [ "vendor/bin/phpunit --colors=always" ] } } ================================================ FILE: docker-compose.yml ================================================ services: package: build: context: . target: php args: COMPOSER_VERSION: "2.3" PHP_VERSION: "8.4" command: ["composer", "test"] volumes: - ./:/package:delegate ================================================ FILE: ecs.php ================================================ paths([ __DIR__ . '/src', __DIR__ . '/tests', ]); $ecsConfig->skip([ __DIR__ . '/tests/Application/config/reference.php', ]); $ecsConfig->import('vendor/sylius-labs/coding-standard/ecs.php'); $ecsConfig->ruleWithConfiguration(HeaderCommentFixer::class, [ 'location' => 'after_open', 'header' => 'This file is part of the Sylius package. (c) Sylius Sp. z o.o. For the full copyright and license information, please view the LICENSE file that was distributed with this source code.', ]) ; $ecsConfig->ruleWithConfiguration(NoSuperfluousPhpdocTagsFixer::class, ['allow_mixed' => true]); $ecsConfig->ruleWithConfiguration(NullableTypeDeclarationForDefaultNullValueFixer::class, ['use_nullable_type_declaration' => true]); $ecsConfig->skip([ InlineDocCommentDeclarationSniff::class . '.MissingVariable', VisibilityRequiredFixer::class => ['*Spec.php'], MethodArgumentSpaceFixer::class => ['*/BoardGameBlog/*', '*/Subscription/*'], 'src/Bundle/Controller/ControllerTrait.php', 'src/Bundle/EventListener/ODMMappedSuperClassSubscriber.php', // hot-fix to fix the build 'src/Component/vendor/*', 'src/Component/Reflection/ClassReflection.php', '**/var/*', ]); }; ================================================ FILE: phpstan-baseline.neon ================================================ parameters: ignoreErrors: - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\AbstractResourceBundle\\:\\:getMappingCompilerPassInfo\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/AbstractResourceBundle.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Command\\\\DebugResourceCommand\\:\\:addResourceOperationsRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Command/DebugResourceCommand.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Command\\\\DebugResourceCommand\\:\\:addResourceOperationsRows\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Command/DebugResourceCommand.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Command\\\\DebugResourceCommand\\:\\:configurationToArray\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Command/DebugResourceCommand.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Command\\\\DebugResourceCommand\\:\\:objectToArray\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Command/DebugResourceCommand.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Command\\\\DebugResourceCommand\\:\\:operationToArray\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Command/DebugResourceCommand.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Command\\\\DebugResourceCommand\\:\\:resourceToArray\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Command/DebugResourceCommand.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Context\\\\Initiator\\\\LegacyRequestContextInitiator\\:\\:resolveVars\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Context/Initiator/LegacyRequestContextInitiator.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Context\\\\Initiator\\\\LegacyRequestContextInitiator\\:\\:resolveVars\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Context/Initiator/LegacyRequestContextInitiator.php - message: "#^Cannot cast mixed to string\\.$#" count: 1 path: src/Bundle/Controller/FlashHelper.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\FlashHelper\\:\\:addFlash\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/FlashHelper.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\FlashHelper\\:\\:getParametersWithName\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/FlashHelper.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\FlashHelper\\:\\:isTranslationDefined\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/FlashHelper.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\FlashHelper\\:\\:prepareMessage\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/FlashHelper.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\FlashHelper\\:\\:prepareMessage\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/FlashHelper.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\NewResourceFactory\\:\\:create\\(\\) has parameter \\$factory with generic interface Sylius\\\\Resource\\\\Factory\\\\FactoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/NewResourceFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\NewResourceFactoryInterface\\:\\:create\\(\\) has parameter \\$factory with generic interface Sylius\\\\Resource\\\\Factory\\\\FactoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/NewResourceFactoryInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ParametersParser\\:\\:parseRequestValues\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ParametersParser.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ParametersParser\\:\\:parseRequestValues\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ParametersParser.php - message: "#^Parameter \\#2 \\$callback of function preg_replace_callback expects callable\\(array\\\\)\\: string, Closure\\(array\\)\\: mixed given\\.$#" count: 1 path: src/Bundle/Controller/ParametersParser.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ParametersParserInterface\\:\\:parseRequestValues\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ParametersParserInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ParametersParserInterface\\:\\:parseRequestValues\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ParametersParserInterface.php - message: "#^Cannot cast mixed to string\\.$#" count: 3 path: src/Bundle/Controller/RedirectHandler.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RedirectHandler\\:\\:redirectToRoute\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RedirectHandler.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RedirectHandlerInterface\\:\\:redirectToRoute\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RedirectHandlerInterface.php - message: "#^Cannot access offset 'arguments' on mixed\\.$#" count: 2 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Cannot access offset 'graph' on mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Cannot access offset 'options' on mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Cannot access offset 'parameters' on mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Cannot access offset 'transition' on mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Cannot access offset 'type' on mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Cannot cast mixed to int\\.$#" count: 2 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:addExtraRedirectParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:addExtraRedirectParameters\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getCriteria\\(\\) has parameter \\$criteria with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getCriteria\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getEvent\\(\\) should return string\\|null but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getFactoryArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getFactoryMethod\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getFormOptions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getFormOptions\\(\\) should return array but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getFormType\\(\\) should return string\\|null but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getGrid\\(\\) should return string but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getPermission\\(\\) should return string but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getRedirectParameters\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getRepositoryArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getRepositoryMethod\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getRequestParameter\\(\\) has parameter \\$defaults with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getRequestParameter\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getSection\\(\\) should return string\\|null but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getSerializationGroups\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getSerializationGroups\\(\\) should return array\\|null but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getSorting\\(\\) has parameter \\$sorting with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getSorting\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getStateMachineGraph\\(\\) should return string\\|null but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getStateMachineTransition\\(\\) should return string\\|null but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getVars\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:getVars\\(\\) should return array but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:isCsrfProtectionEnabled\\(\\) should return bool but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:parseResourceValues\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfiguration\\:\\:parseResourceValues\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^PHPDoc tag @var for variable \\$redirect has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, mixed given\\.$#" count: 2 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Parameter \\#2 \\.\\.\\.\\$replacements of function array_replace_recursive expects array, mixed given\\.$#" count: 1 path: src/Bundle/Controller/RequestConfiguration.php - message: "#^Cannot access offset 'allowed…' on mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfigurationFactory.php - message: "#^Cannot access offset 'serialization_groups' on mixed\\.$#" count: 2 path: src/Bundle/Controller/RequestConfigurationFactory.php - message: "#^Cannot access offset 'serialization…' on mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfigurationFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfigurationFactory\\:\\:__construct\\(\\) has parameter \\$defaultParameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfigurationFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfigurationFactory\\:\\:parseApiParameters\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfigurationFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfigurationFactory\\:\\:parseApiParameters\\(\\) should return array but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/RequestConfigurationFactory.php - message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, mixed given\\.$#" count: 1 path: src/Bundle/Controller/RequestConfigurationFactory.php - message: "#^Parameter \\#2 \\.\\.\\.\\$arrays of function array_merge expects array, mixed given\\.$#" count: 1 path: src/Bundle/Controller/RequestConfigurationFactory.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RequestConfigurationFactory\\:\\:\\$defaultParameters type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/RequestConfigurationFactory.php - message: "#^Cannot call method create\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method createBuilder\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method dispatch\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method display\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method generate\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method getCurrentRequest\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method getSession\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method getToken\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method handle\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method isGranted\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method isTokenValid\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method render\\(\\) on mixed\\.$#" count: 4 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method serialize\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method stream\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Cannot call method withLink\\(\\) on mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:__construct\\(\\) has parameter \\$factory with generic interface Sylius\\\\Resource\\\\Factory\\\\FactoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:__construct\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:addFlash\\(\\) has no return type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:addFlash\\(\\) has parameter \\$message with no type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:addLink\\(\\) has no return type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:createForm\\(\\) has parameter \\$data with no type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:createForm\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:createFormBuilder\\(\\) has parameter \\$data with no type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:createFormBuilder\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:denyAccessUnlessGranted\\(\\) has no return type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:denyAccessUnlessGranted\\(\\) has parameter \\$attributes with no type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:denyAccessUnlessGranted\\(\\) has parameter \\$subject with no type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:forward\\(\\) has parameter \\$path with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:forward\\(\\) has parameter \\$query with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:generateUrl\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:get\\(\\) should return object but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:getDoctrine\\(\\) should return Doctrine\\\\Persistence\\\\ManagerRegistry but returns mixed\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:isGranted\\(\\) has parameter \\$attributes with no type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:isGranted\\(\\) has parameter \\$subject with no type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:json\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:json\\(\\) has parameter \\$data with no type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:json\\(\\) has parameter \\$headers with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:redirectToRoute\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:render\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:renderView\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:stream\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Parameter \\#1 \\$resource of method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceUpdateHandlerInterface\\:\\:handle\\(\\) expects Sylius\\\\Resource\\\\Model\\\\ResourceInterface, mixed given\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Parameter \\#1 \\$resource of method Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface\\\\:\\:add\\(\\) expects Sylius\\\\Resource\\\\Model\\\\ResourceInterface, mixed given\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Parameter \\#1 \\$view of method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:render\\(\\) expects string, mixed given\\.$#" count: 4 path: src/Bundle/Controller/ResourceController.php - message: "#^Parameter \\#2 \\$resource of method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RedirectHandlerInterface\\:\\:redirectToIndex\\(\\) expects Sylius\\\\Resource\\\\Model\\\\ResourceInterface\\|null, mixed given\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Parameter \\#2 \\$resource of method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\RedirectHandlerInterface\\:\\:redirectToResource\\(\\) expects Sylius\\\\Resource\\\\Model\\\\ResourceInterface, mixed given\\.$#" count: 3 path: src/Bundle/Controller/ResourceController.php - message: "#^Parameter \\#2 \\$resource of method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\StateMachineInterface\\:\\:apply\\(\\) expects Sylius\\\\Resource\\\\Model\\\\ResourceInterface, mixed given\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Parameter \\#2 \\$token of method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:isCsrfTokenValid\\(\\) expects string\\|null, mixed given\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Parameter \\#3 \\$resource of method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\EventDispatcherInterface\\:\\:dispatchPostEvent\\(\\) expects Sylius\\\\Resource\\\\Model\\\\ResourceInterface, mixed given\\.$#" count: 2 path: src/Bundle/Controller/ResourceController.php - message: "#^Parameter \\#3 \\$resource of method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\EventDispatcherInterface\\:\\:dispatchPreEvent\\(\\) expects Sylius\\\\Resource\\\\Model\\\\ResourceInterface, mixed given\\.$#" count: 2 path: src/Bundle/Controller/ResourceController.php - message: "#^Parameter \\#3 \\$resource of method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\FlashHelperInterface\\:\\:addSuccessFlash\\(\\) expects Sylius\\\\Resource\\\\Model\\\\ResourceInterface\\|null, mixed given\\.$#" count: 2 path: src/Bundle/Controller/ResourceController.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:\\$container \\(Psr\\\\Container\\\\ContainerInterface\\) does not accept Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:\\$factory with generic interface Sylius\\\\Resource\\\\Factory\\\\FactoryInterface does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceController\\:\\:\\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/ResourceController.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceDeleteHandler\\:\\:handle\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/ResourceDeleteHandler.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourceDeleteHandlerInterface\\:\\:handle\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/ResourceDeleteHandlerInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourcesCollectionProvider\\:\\:get\\(\\) has no return type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourcesCollectionProvider.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourcesCollectionProvider\\:\\:get\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/ResourcesCollectionProvider.php - message: "#^Parameter \\#1 \\$name of class Hateoas\\\\Configuration\\\\Route constructor expects JMS\\\\Serializer\\\\Expression\\\\Expression\\|string, mixed given\\.$#" count: 1 path: src/Bundle/Controller/ResourcesCollectionProvider.php - message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, mixed given\\.$#" count: 1 path: src/Bundle/Controller/ResourcesCollectionProvider.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourcesCollectionProviderInterface\\:\\:get\\(\\) has no return type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourcesCollectionProviderInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourcesCollectionProviderInterface\\:\\:get\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/ResourcesCollectionProviderInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourcesResolver\\:\\:getResources\\(\\) has no return type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourcesResolver.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourcesResolver\\:\\:getResources\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/ResourcesResolver.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourcesResolverInterface\\:\\:getResources\\(\\) has no return type specified\\.$#" count: 1 path: src/Bundle/Controller/ResourcesResolverInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\ResourcesResolverInterface\\:\\:getResources\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/ResourcesResolverInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\SingleResourceProvider\\:\\:get\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/SingleResourceProvider.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Controller\\\\SingleResourceProviderInterface\\:\\:get\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Controller/SingleResourceProviderInterface.php - message: "#^Parameter \\#1 \\$version of method FOS\\\\RestBundle\\\\View\\\\ConfigurableViewHandlerInterface\\:\\:setExclusionStrategyVersion\\(\\) expects string, mixed given\\.$#" count: 1 path: src/Bundle/Controller/ViewHandler.php - message: "#^PHPDoc tag @var for variable \\$resources has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/DoctrineTargetEntitiesResolverPass.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\Compiler\\\\Helper\\\\TargetEntitiesResolver\\:\\:getModel\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/Helper/TargetEntitiesResolver.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\Compiler\\\\Helper\\\\TargetEntitiesResolver\\:\\:resolve\\(\\) has parameter \\$resourcesConfiguration with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/Helper/TargetEntitiesResolver.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\Compiler\\\\Helper\\\\TargetEntitiesResolver\\:\\:resolve\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/Helper/TargetEntitiesResolver.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\Compiler\\\\Helper\\\\TargetEntitiesResolverInterface\\:\\:resolve\\(\\) has parameter \\$resourcesConfiguration with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/Helper/TargetEntitiesResolverInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\Compiler\\\\Helper\\\\TargetEntitiesResolverInterface\\:\\:resolve\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/Helper/TargetEntitiesResolverInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\Compiler\\\\PrioritizedCompositeServicePass\\:\\:addMethodCall\\(\\) has parameter \\$attributes with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/PrioritizedCompositeServicePass.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\Compiler\\\\PrioritizedCompositeServicePass\\:\\:addMethodCalls\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/PrioritizedCompositeServicePass.php - message: "#^PHPDoc tag @var for variable \\$resources has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/RegisterFqcnControllersPass.php - message: "#^PHPDoc tag @var for variable \\$resources has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/RegisterResourceRepositoryPass.php - message: "#^PHPDoc tag @var for variable \\$resources has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/RegisterResourceStateMachinePass.php - message: "#^PHPDoc tag @var for variable \\$interfaces has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/RegisterResourcesPass.php - message: "#^PHPDoc tag @var for variable \\$resources has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/RegisterResourcesPass.php - message: "#^PHPDoc tag @var for variable \\$bundles has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/RegisterStateMachinePass.php - message: "#^PHPDoc tag @var for variable \\$settings has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/RegisterStateMachinePass.php - message: "#^PHPDoc tag @var for variable \\$bundles has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/UnregisterFosRestDefinitionsPass.php - message: "#^PHPDoc tag @var for variable \\$bundles has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/UnregisterHateoasDefinitionsPass.php - message: "#^PHPDoc tag @var for variable \\$bundles has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Compiler/WinzouStateMachinePass.php - message: "#^Cannot call method end\\(\\) on Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\|null\\.$#" count: 3 path: src/Bundle/DependencyInjection/Configuration.php - message: "#^Cannot call method variableNode\\(\\) on Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\|null\\.$#" count: 2 path: src/Bundle/DependencyInjection/Configuration.php - message: "#^PHPDoc tag @var for variable \\$factoryInterfaces has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Driver/AbstractDriver.php - message: "#^PHPDoc tag @var for variable \\$factoryParents has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Driver/AbstractDriver.php - message: "#^PHPDoc tag @var for variable \\$repositoryInterfaces has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Driver/Doctrine/DoctrineORMDriver.php - message: "#^PHPDoc tag @var for variable \\$repositoryParents has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Driver/Doctrine/DoctrineORMDriver.php - message: "#^Parameter \\#1 \\$array of function array_keys expects array, array\\|string given\\.$#" count: 1 path: src/Bundle/DependencyInjection/Driver/Doctrine/DoctrinePHPCRDriver.php - message: "#^Parameter \\#1 \\$class of class Symfony\\\\Component\\\\DependencyInjection\\\\Definition constructor expects string\\|null, class\\-string\\|Symfony\\\\Component\\\\DependencyInjection\\\\Parameter given\\.$#" count: 1 path: src/Bundle/DependencyInjection/Driver/Doctrine/DoctrinePHPCRDriver.php - message: "#^Parameter \\#2 \\.\\.\\.\\$arrays of function array_merge expects array, array\\|string given\\.$#" count: 1 path: src/Bundle/DependencyInjection/Driver/Doctrine/DoctrinePHPCRDriver.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\Extension\\\\AbstractResourceExtension\\:\\:registerResources\\(\\) has parameter \\$registeredResources with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Extension/AbstractResourceExtension.php - message: "#^PHPDoc tag @var for variable \\$resources has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/Extension/AbstractResourceExtension.php - message: "#^Cannot call method end\\(\\) on Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\|null\\.$#" count: 1 path: src/Bundle/DependencyInjection/PagerfantaConfiguration.php - message: "#^Cannot call method scalarNode\\(\\) on Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\|null\\.$#" count: 1 path: src/Bundle/DependencyInjection/PagerfantaConfiguration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\PagerfantaExtension\\:\\:getConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/PagerfantaExtension.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\SyliusResourceExtension\\:\\:autoRegisterResources\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/SyliusResourceExtension.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\SyliusResourceExtension\\:\\:getAvailableDrivers\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/SyliusResourceExtension.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\SyliusResourceExtension\\:\\:getConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/SyliusResourceExtension.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\SyliusResourceExtension\\:\\:getResourceFilesToWatch\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/SyliusResourceExtension.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\SyliusResourceExtension\\:\\:getResourceFilesToWatch\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/SyliusResourceExtension.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\SyliusResourceExtension\\:\\:loadPersistence\\(\\) has parameter \\$drivers with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/SyliusResourceExtension.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\SyliusResourceExtension\\:\\:loadResources\\(\\) has parameter \\$loadedResources with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/SyliusResourceExtension.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\DependencyInjection\\\\SyliusResourceExtension\\:\\:registerMetadataConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/SyliusResourceExtension.php - message: "#^PHPDoc tag @var for variable \\$mapping has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/DependencyInjection/SyliusResourceExtension.php - message: "#^PHPDoc tag @var for variable \\$resources has no value type specified in iterable type array\\.$#" count: 2 path: src/Bundle/DependencyInjection/SyliusResourceExtension.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\ContainerRepositoryFactory\\:\\:getOrCreateRepository\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ORM/ContainerRepositoryFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\ContainerRepositoryFactory\\:\\:getOrCreateRepository\\(\\) return type with generic class Doctrine\\\\ORM\\\\EntityRepository does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ORM/ContainerRepositoryFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\ContainerRepositoryFactory\\:\\:getRepository\\(\\) return type with generic class Doctrine\\\\ORM\\\\EntityRepository does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ORM/ContainerRepositoryFactory.php - message: "#^PHPDoc tag @var for variable \\$repository contains generic class Doctrine\\\\ORM\\\\EntityRepository but does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ORM/ContainerRepositoryFactory.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\ContainerRepositoryFactory\\:\\:\\$managedRepositories with generic class Doctrine\\\\ORM\\\\EntityRepository does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ORM/ContainerRepositoryFactory.php - message: "#^Class Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\EntityRepository extends generic class Doctrine\\\\ORM\\\\EntityRepository but does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ORM/EntityRepository.php - message: "#^Class Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\EntityRepository implements generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ORM/EntityRepository.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\EntityRepository\\:\\:applyCriteria\\(\\) has parameter \\$criteria with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Doctrine/ORM/EntityRepository.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\EntityRepository\\:\\:applySorting\\(\\) has parameter \\$sorting with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Doctrine/ORM/EntityRepository.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\EntityRepository\\:\\:getArrayPaginator\\(\\) has parameter \\$objects with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Doctrine/ORM/EntityRepository.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\EntityRepository\\:\\:getArrayPaginator\\(\\) return type with generic interface Pagerfanta\\\\PagerfantaInterface does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ORM/EntityRepository.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\EntityRepository\\:\\:getPaginator\\(\\) return type with generic interface Pagerfanta\\\\PagerfantaInterface does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ORM/EntityRepository.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\Form\\\\Builder\\\\DefaultFormBuilder\\:\\:build\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Doctrine/ORM/Form/Builder/DefaultFormBuilder.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ORM\\\\Form\\\\Builder\\\\DefaultFormBuilder\\:\\:doBuild\\(\\) has parameter \\$classMetadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ORM/Form/Builder/DefaultFormBuilder.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Doctrine\\\\ResourceMappingDriverChain\\:\\:convertResourceMappedSuperclass\\(\\) has parameter \\$metadata with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#" count: 1 path: src/Bundle/Doctrine/ResourceMappingDriverChain.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\EventListener\\\\AbstractDoctrineListener\\:\\:isResource\\(\\) has parameter \\$metadata with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#" count: 1 path: src/Bundle/EventListener/AbstractDoctrineListener.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\EventListener\\\\ORMMappedSuperClassSubscriber\\:\\:setAssociationMappings\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#" count: 1 path: src/Bundle/EventListener/ORMMappedSuperClassSubscriber.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\EventListener\\\\ORMMappedSuperClassSubscriber\\:\\:unsetAssociationMappings\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#" count: 1 path: src/Bundle/EventListener/ORMMappedSuperClassSubscriber.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\EventListener\\\\ORMRepositoryClassSubscriber\\:\\:setCustomRepositoryClass\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#" count: 1 path: src/Bundle/EventListener/ORMRepositoryClassSubscriber.php - message: "#^PHPDoc tag @var for variable \\$repository contains generic class Doctrine\\\\ORM\\\\EntityRepository but does not specify its types\\: T$#" count: 1 path: src/Bundle/EventListener/ORMRepositoryClassSubscriber.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\EventListener\\\\ORMTranslatableListener\\:\\:hasUniqueConstraint\\(\\) has parameter \\$columns with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/EventListener/ORMTranslatableListener.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\EventListener\\\\ORMTranslatableListener\\:\\:hasUniqueConstraint\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#" count: 1 path: src/Bundle/EventListener/ORMTranslatableListener.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\EventListener\\\\ORMTranslatableListener\\:\\:mapTranslatable\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#" count: 1 path: src/Bundle/EventListener/ORMTranslatableListener.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\EventListener\\\\ORMTranslatableListener\\:\\:mapTranslation\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#" count: 1 path: src/Bundle/EventListener/ORMTranslatableListener.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\ExpressionLanguage\\\\ExpressionLanguage\\:\\:__construct\\(\\) has parameter \\$providers with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/ExpressionLanguage/ExpressionLanguage.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\Builder\\\\DefaultFormBuilderInterface\\:\\:build\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Form/Builder/DefaultFormBuilderInterface.php - message: "#^Class Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\CollectionToStringTransformer implements generic interface Symfony\\\\Component\\\\Form\\\\DataTransformerInterface but does not specify its types\\: TValue, TTransformedValue$#" count: 1 path: src/Bundle/Form/DataTransformer/CollectionToStringTransformer.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\CollectionToStringTransformer\\:\\:reverseTransform\\(\\) return type with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection does not specify its types\\: TKey, T$#" count: 1 path: src/Bundle/Form/DataTransformer/CollectionToStringTransformer.php - message: "#^Class Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\RecursiveTransformer implements generic interface Symfony\\\\Component\\\\Form\\\\DataTransformerInterface but does not specify its types\\: TValue, TTransformedValue$#" count: 1 path: src/Bundle/Form/DataTransformer/RecursiveTransformer.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\RecursiveTransformer\\:\\:__construct\\(\\) has parameter \\$decoratedTransformer with generic interface Symfony\\\\Component\\\\Form\\\\DataTransformerInterface but does not specify its types\\: TValue, TTransformedValue$#" count: 1 path: src/Bundle/Form/DataTransformer/RecursiveTransformer.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\RecursiveTransformer\\:\\:reverseTransform\\(\\) has parameter \\$value with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection but does not specify its types\\: TKey, T$#" count: 1 path: src/Bundle/Form/DataTransformer/RecursiveTransformer.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\RecursiveTransformer\\:\\:reverseTransform\\(\\) return type with generic interface Doctrine\\\\Common\\\\Collections\\\\ReadableCollection does not specify its types\\: TKey, T$#" count: 1 path: src/Bundle/Form/DataTransformer/RecursiveTransformer.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\RecursiveTransformer\\:\\:transform\\(\\) has parameter \\$value with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection but does not specify its types\\: TKey, T$#" count: 1 path: src/Bundle/Form/DataTransformer/RecursiveTransformer.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\RecursiveTransformer\\:\\:transform\\(\\) return type with generic interface Doctrine\\\\Common\\\\Collections\\\\ReadableCollection does not specify its types\\: TKey, T$#" count: 1 path: src/Bundle/Form/DataTransformer/RecursiveTransformer.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\RecursiveTransformer\\:\\:\\$decoratedTransformer with generic interface Symfony\\\\Component\\\\Form\\\\DataTransformerInterface does not specify its types\\: TValue, TTransformedValue$#" count: 1 path: src/Bundle/Form/DataTransformer/RecursiveTransformer.php - message: "#^Class Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\ResourceToIdentifierTransformer implements generic interface Symfony\\\\Component\\\\Form\\\\DataTransformerInterface but does not specify its types\\: TValue, TTransformedValue$#" count: 1 path: src/Bundle/Form/DataTransformer/ResourceToIdentifierTransformer.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\ResourceToIdentifierTransformer\\:\\:__construct\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Form/DataTransformer/ResourceToIdentifierTransformer.php - message: "#^Parameter \\#4 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" count: 1 path: src/Bundle/Form/DataTransformer/ResourceToIdentifierTransformer.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\DataTransformer\\\\ResourceToIdentifierTransformer\\:\\:\\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface does not specify its types\\: T$#" count: 1 path: src/Bundle/Form/DataTransformer/ResourceToIdentifierTransformer.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\EventSubscriber\\\\AddCodeFormSubscriber\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Form/EventSubscriber/AddCodeFormSubscriber.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\EventSubscriber\\\\AddCodeFormSubscriber\\:\\:\\$options type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Form/EventSubscriber/AddCodeFormSubscriber.php - message: "#^PHPDoc tag @var for variable \\$default has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\Registry\\\\FormTypeRegistry\\:\\:\\$formTypes type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Form/Registry/FormTypeRegistry.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\Type\\\\ResourceToIdentifierType\\:\\:__construct\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Form/Type/ResourceToIdentifierType.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Form\\\\Type\\\\ResourceToIdentifierType\\:\\:\\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface does not specify its types\\: T$#" count: 1 path: src/Bundle/Form/Type/ResourceToIdentifierType.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Controller\\\\ResourcesResolver\\:\\:getResources\\(\\) has no return type specified\\.$#" count: 1 path: src/Bundle/Grid/Controller/ResourcesResolver.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Controller\\\\ResourcesResolver\\:\\:getResources\\(\\) has parameter \\$repository with generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Bundle/Grid/Controller/ResourcesResolver.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Parser\\\\OptionsParser\\:\\:parseOption\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Parser/OptionsParser.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Parser\\\\OptionsParser\\:\\:parseOptionResourceField\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Parser/OptionsParser.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Parser\\\\OptionsParser\\:\\:parseOptions\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Parser/OptionsParser.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Parser\\\\OptionsParser\\:\\:parseOptions\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Parser/OptionsParser.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Parser\\\\OptionsParser\\:\\:parseOptions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Parser/OptionsParser.php - message: "#^Parameter \\#1 \\$objectOrArray of method Symfony\\\\Component\\\\PropertyAccess\\\\PropertyAccessorInterface\\:\\:getValue\\(\\) expects array\\|object, array\\|object\\|null given\\.$#" count: 1 path: src/Bundle/Grid/Parser/OptionsParser.php - message: "#^Parameter \\#2 \\$callback of function preg_replace_callback expects callable\\(array\\\\)\\: string, Closure\\(array\\)\\: mixed given\\.$#" count: 1 path: src/Bundle/Grid/Parser/OptionsParser.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Parser\\\\OptionsParserInterface\\:\\:parseOptions\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Parser/OptionsParserInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Parser\\\\OptionsParserInterface\\:\\:parseOptions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Parser/OptionsParserInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Renderer\\\\TwigBulkActionGridRenderer\\:\\:__construct\\(\\) has parameter \\$bulkActionTemplates with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Renderer/TwigBulkActionGridRenderer.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Renderer\\\\TwigBulkActionGridRenderer\\:\\:\\$bulkActionTemplates type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Renderer/TwigBulkActionGridRenderer.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Renderer\\\\TwigGridRenderer\\:\\:__construct\\(\\) has parameter \\$actionTemplates with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Renderer/TwigGridRenderer.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\Renderer\\\\TwigGridRenderer\\:\\:\\$actionTemplates type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/Renderer/TwigGridRenderer.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Grid\\\\View\\\\LegacyGridViewFactory\\:\\:create\\(\\) has parameter \\$driverConfiguration with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Grid/View/LegacyGridViewFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\ResourceBundleInterface\\:\\:getSupportedDrivers\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/ResourceBundleInterface.php - message: "#^Cannot call method scalarNode\\(\\) on Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\|null\\.$#" count: 1 path: src/Bundle/Routing/Configuration.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\CrudRoutesAttributesLoader\\:\\:__construct\\(\\) has parameter \\$mapping with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/CrudRoutesAttributesLoader.php - message: "#^Property Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\CrudRoutesAttributesLoader\\:\\:\\$mapping type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/CrudRoutesAttributesLoader.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\ResourceLoader\\:\\:createRoute\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/ResourceLoader.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\ResourceLoader\\:\\:createRoute\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/ResourceLoader.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\ResourceLoader\\:\\:getRouteName\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/ResourceLoader.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\ResourceLoader\\:\\:load\\(\\) has parameter \\$resource with no type specified\\.$#" count: 1 path: src/Bundle/Routing/ResourceLoader.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\ResourceLoader\\:\\:load\\(\\) has parameter \\$type with no type specified\\.$#" count: 1 path: src/Bundle/Routing/ResourceLoader.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\ResourceLoader\\:\\:supports\\(\\) has parameter \\$type with no type specified\\.$#" count: 1 path: src/Bundle/Routing/ResourceLoader.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RouteFactory\\:\\:createRoute\\(\\) has parameter \\$defaults with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RouteFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RouteFactory\\:\\:createRoute\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RouteFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RouteFactory\\:\\:createRoute\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RouteFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RouteFactory\\:\\:createRoute\\(\\) has parameter \\$requirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RouteFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RouteFactory\\:\\:createRoute\\(\\) has parameter \\$schemes with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RouteFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RouteFactoryInterface\\:\\:createRoute\\(\\) has parameter \\$defaults with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RouteFactoryInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RouteFactoryInterface\\:\\:createRoute\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RouteFactoryInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RouteFactoryInterface\\:\\:createRoute\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RouteFactoryInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RouteFactoryInterface\\:\\:createRoute\\(\\) has parameter \\$requirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RouteFactoryInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RouteFactoryInterface\\:\\:createRoute\\(\\) has parameter \\$schemes with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RouteFactoryInterface.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Routing\\\\RoutesAttributesLoader\\:\\:__construct\\(\\) has parameter \\$mapping with no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Routing/RoutesAttributesLoader.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Storage\\\\CookieStorage\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Storage/CookieStorage.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Storage\\\\SessionStorage\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Storage/SessionStorage.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Twig\\\\Context\\\\LegacyContextFactory\\:\\:create\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Twig/Context/LegacyContextFactory.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Validator\\\\Constraints\\\\Disabled\\:\\:getTargets\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Validator/Constraints/Disabled.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Validator\\\\Constraints\\\\Enabled\\:\\:getTargets\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Bundle/Validator/Constraints/Enabled.php - message: "#^Method Sylius\\\\Bundle\\\\ResourceBundle\\\\Validator\\\\UniqueWithinCollectionConstraintValidator\\:\\:validate\\(\\) has parameter \\$value with no value type specified in iterable type iterable\\.$#" count: 1 path: src/Bundle/Validator/UniqueWithinCollectionConstraintValidator.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusCrudRoutes\\:\\:__construct\\(\\) has parameter \\$criteria with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusCrudRoutes.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusCrudRoutes\\:\\:__construct\\(\\) has parameter \\$except with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusCrudRoutes.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusCrudRoutes\\:\\:__construct\\(\\) has parameter \\$only with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusCrudRoutes.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusCrudRoutes\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusCrudRoutes.php - message: "#^Property Sylius\\\\Resource\\\\Annotation\\\\SyliusCrudRoutes\\:\\:\\$criteria type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusCrudRoutes.php - message: "#^Property Sylius\\\\Resource\\\\Annotation\\\\SyliusCrudRoutes\\:\\:\\$except type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusCrudRoutes.php - message: "#^Property Sylius\\\\Resource\\\\Annotation\\\\SyliusCrudRoutes\\:\\:\\$only type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusCrudRoutes.php - message: "#^Property Sylius\\\\Resource\\\\Annotation\\\\SyliusCrudRoutes\\:\\:\\$vars type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusCrudRoutes.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$criteria with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$form with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$redirect with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$repository with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$requirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$schemes with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$serializationGroups with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$stateMachine with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Method Sylius\\\\Resource\\\\Annotation\\\\SyliusRoute\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Annotation/SyliusRoute.php - message: "#^Class Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\InMemoryRepository implements generic interface Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\RepositoryInterface but does not specify its types\\: T$#" count: 1 path: src/Component/src/Doctrine/Persistence/InMemoryRepository.php - message: "#^Method Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\InMemoryRepository\\:\\:applyCriteria\\(\\) has parameter \\$criteria with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Doctrine/Persistence/InMemoryRepository.php - message: "#^Method Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\InMemoryRepository\\:\\:applyOrder\\(\\) has parameter \\$orderBy with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Doctrine/Persistence/InMemoryRepository.php - message: "#^Method Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\InMemoryRepository\\:\\:createPaginator\\(\\) return type with generic interface Pagerfanta\\\\PagerfantaInterface does not specify its types\\: T$#" count: 1 path: src/Component/src/Doctrine/Persistence/InMemoryRepository.php - message: "#^Method Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\InMemoryRepository\\:\\:findBy\\(\\) has parameter \\$limit with no type specified\\.$#" count: 1 path: src/Component/src/Doctrine/Persistence/InMemoryRepository.php - message: "#^Method Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\InMemoryRepository\\:\\:findBy\\(\\) has parameter \\$offset with no type specified\\.$#" count: 1 path: src/Component/src/Doctrine/Persistence/InMemoryRepository.php - message: "#^PHPDoc tag @var for variable \\$interfaceInterfaces has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Doctrine/Persistence/InMemoryRepository.php - message: "#^PHPDoc tag @var for variable \\$object has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Doctrine/Persistence/InMemoryRepository.php - message: "#^Parameter \\#1 \\$array of function array_multisort expects array, array\\|int given\\.$#" count: 1 path: src/Component/src/Doctrine/Persistence/InMemoryRepository.php - message: "#^Property Sylius\\\\Resource\\\\Doctrine\\\\Persistence\\\\InMemoryRepository\\:\\:\\$arrayObject with generic class ArrayObject does not specify its types\\: TKey, TValue$#" count: 1 path: src/Component/src/Doctrine/Persistence/InMemoryRepository.php - message: "#^Class Sylius\\\\Resource\\\\Factory\\\\Factory implements generic interface Sylius\\\\Resource\\\\Factory\\\\FactoryInterface but does not specify its types\\: T$#" count: 1 path: src/Component/src/Factory/Factory.php - message: "#^Class Sylius\\\\Resource\\\\Factory\\\\TranslatableFactory implements generic interface Sylius\\\\Resource\\\\Factory\\\\TranslatableFactoryInterface but does not specify its types\\: T$#" count: 1 path: src/Component/src/Factory/TranslatableFactory.php - message: "#^Method Sylius\\\\Resource\\\\Factory\\\\TranslatableFactory\\:\\:__construct\\(\\) has parameter \\$factory with generic interface Sylius\\\\Resource\\\\Factory\\\\FactoryInterface but does not specify its types\\: T$#" count: 1 path: src/Component/src/Factory/TranslatableFactory.php - message: "#^Property Sylius\\\\Resource\\\\Factory\\\\TranslatableFactory\\:\\:\\$factory with generic interface Sylius\\\\Resource\\\\Factory\\\\FactoryInterface does not specify its types\\: T$#" count: 1 path: src/Component/src/Factory/TranslatableFactory.php - message: "#^Interface Sylius\\\\Resource\\\\Factory\\\\TranslatableFactoryInterface extends generic interface Sylius\\\\Resource\\\\Factory\\\\FactoryInterface but does not specify its types\\: T$#" count: 1 path: src/Component/src/Factory/TranslatableFactoryInterface.php - message: "#^Method Sylius\\\\Resource\\\\Grid\\\\State\\\\RequestGridProvider\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Grid/State/RequestGridProvider.php - message: "#^Method Sylius\\\\Resource\\\\Grid\\\\State\\\\RequestGridProvider\\:\\:resolveMaxPerPage\\(\\) has parameter \\$gridLimits with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Grid/State/RequestGridProvider.php - message: "#^Method Sylius\\\\Resource\\\\Grid\\\\View\\\\Factory\\\\GridViewFactory\\:\\:create\\(\\) has parameter \\$driverConfiguration with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Grid/View/Factory/GridViewFactory.php - message: "#^Method Sylius\\\\Resource\\\\Grid\\\\View\\\\Factory\\\\GridViewFactoryInterface\\:\\:create\\(\\) has parameter \\$driverConfiguration with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Grid/View/Factory/GridViewFactoryInterface.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Delete\\:\\:__construct\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Delete\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Delete\\:\\:__construct\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Delete\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Delete\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Delete\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Get\\:\\:__construct\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Get.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Get\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Get.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Get\\:\\:__construct\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Get.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Get\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Get.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Get\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Get.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Get\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Get.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\GetCollection\\:\\:__construct\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/GetCollection.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\GetCollection\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/GetCollection.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\GetCollection\\:\\:__construct\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/GetCollection.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\GetCollection\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/GetCollection.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\GetCollection\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/GetCollection.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\GetCollection\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/GetCollection.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Patch\\:\\:__construct\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Patch.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Patch\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Patch.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Patch\\:\\:__construct\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Patch.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Patch\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Patch.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Patch\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Patch.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Patch\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Patch.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Post\\:\\:__construct\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Post.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Post\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Post.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Post\\:\\:__construct\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Post.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Post\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Post.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Post\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Post.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Post\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Post.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Put\\:\\:__construct\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Put.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Put\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Put.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Put\\:\\:__construct\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Put.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Put\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Put.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Put\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Put.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Api\\\\Put\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Api/Put.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ApplyStateMachineTransition\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ApplyStateMachineTransition.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ApplyStateMachineTransition\\:\\:__construct\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ApplyStateMachineTransition.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ApplyStateMachineTransition\\:\\:__construct\\(\\) has parameter \\$redirectArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ApplyStateMachineTransition.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ApplyStateMachineTransition\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ApplyStateMachineTransition.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\AsResource\\:\\:__construct\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/AsResource.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\AsResource\\:\\:__construct\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/AsResource.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\AsResource\\:\\:__construct\\(\\) has parameter \\$operations with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/AsResource.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\AsResource\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/AsResource.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\AsResource\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/AsResource.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkDelete\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkDelete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkDelete\\:\\:__construct\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkDelete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkDelete\\:\\:__construct\\(\\) has parameter \\$redirectArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkDelete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkDelete\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkDelete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkDelete\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkDelete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkDelete\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkDelete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkUpdate\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkUpdate.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkUpdate\\:\\:__construct\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkUpdate.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkUpdate\\:\\:__construct\\(\\) has parameter \\$redirectArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkUpdate.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkUpdate\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkUpdate.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkUpdate\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkUpdate.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkUpdate\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkUpdate.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\BulkUpdate\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/BulkUpdate.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Create\\:\\:__construct\\(\\) has parameter \\$factoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Create.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Create\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Create.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Create\\:\\:__construct\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Create.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Create\\:\\:__construct\\(\\) has parameter \\$redirectArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Create.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Create\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Create.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Create\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Create.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Create\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Create.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Create\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Create.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Create\\:\\:getFactoryArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Create.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Create\\:\\:withFactoryArguments\\(\\) has parameter \\$factoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Create.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Delete\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Delete\\:\\:__construct\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Delete\\:\\:__construct\\(\\) has parameter \\$redirectArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Delete\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Delete\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Delete\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Delete\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Delete.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Extractor\\\\AbstractResourceExtractor\\:\\:getResources\\(\\) should return array\\ but returns array\\\\|null\\.$#" count: 1 path: src/Component/src/Metadata/Extractor/AbstractResourceExtractor.php - message: "#^Property Sylius\\\\Resource\\\\Metadata\\\\Extractor\\\\AbstractResourceExtractor\\:\\:\\$collectedParameters type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Extractor/AbstractResourceExtractor.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\FactoryAwareOperationInterface\\:\\:getFactoryArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/FactoryAwareOperationInterface.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\FactoryAwareOperationInterface\\:\\:withFactoryArguments\\(\\) has parameter \\$factoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/FactoryAwareOperationInterface.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:__construct\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:__construct\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:__construct\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:__construct\\(\\) has parameter \\$redirectArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:getMethods\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:getRedirectArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:getRouteRequirements\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:getVars\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:withMethods\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:withRedirectArguments\\(\\) has parameter \\$redirectArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:withRouteRequirements\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\HttpOperation\\:\\:withVars\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/HttpOperation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Index\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Index.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Index\\:\\:__construct\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Index.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Index\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Index.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Index\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Index.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Index\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Index.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Index\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Index.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Metadata\\:\\:__construct\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Metadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Metadata\\:\\:fromAliasAndConfiguration\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Metadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Metadata\\:\\:getParameter\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Metadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Metadata\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Metadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Metadata\\:\\:parseAlias\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Metadata.php - message: "#^Property Sylius\\\\Resource\\\\Metadata\\\\Metadata\\:\\:\\$parameters type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Metadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\MetadataInterface\\:\\:getParameter\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/MetadataInterface.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\MetadataInterface\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/MetadataInterface.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:__construct\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:__construct\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:getDenormalizationContext\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:getFormOptions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:getNormalizationContext\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:getRepositoryArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:getValidationContext\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:withDenormalizationContext\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:withFormOptions\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:withNormalizationContext\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:withRepositoryArguments\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\:\\:withValidationContext\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\\\HttpOperationInitiator\\:\\:resolveVars\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation/HttpOperationInitiator.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Operation\\\\HttpOperationInitiator\\:\\:resolveVars\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operation/HttpOperationInitiator.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\OperationAccessCheckerInterface\\:\\:isGranted\\(\\) has parameter \\$extraVariables with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/OperationAccessCheckerInterface.php - message: "#^Class Sylius\\\\Resource\\\\Metadata\\\\Operations implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" count: 1 path: src/Component/src/Metadata/Operations.php - message: "#^Property Sylius\\\\Resource\\\\Metadata\\\\Operations\\:\\:\\$operations type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Operations.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Registry\\:\\:addFromAliasAndConfiguration\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Registry.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\RegistryInterface\\:\\:addFromAliasAndConfiguration\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/RegistryInterface.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Resource\\\\Factory\\\\AttributesResourceMetadataCollectionFactory\\:\\:buildFormOptions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Resource\\\\Factory\\\\AttributesResourceMetadataCollectionFactory\\:\\:buildResourceOperations\\(\\) has parameter \\$attributes with generic class ReflectionAttribute but does not specify its types\\: T$#" count: 1 path: src/Component/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Resource\\\\Factory\\\\AttributesResourceMetadataCollectionFactory\\:\\:getOperationWithDefaults\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php - message: "#^Property Sylius\\\\Resource\\\\Metadata\\\\Resource\\\\Factory\\\\CachedResourceMetadataCollectionFactory\\:\\:\\$localCache type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Resource/Factory/CachedResourceMetadataCollectionFactory.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Resource\\\\Factory\\\\PhpFileResourceMetadataCollectionFactory\\:\\:buildFormOptions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Resource/Factory/PhpFileResourceMetadataCollectionFactory.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Resource\\\\Factory\\\\PhpFileResourceMetadataCollectionFactory\\:\\:getOperationWithDefaults\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Resource/Factory/PhpFileResourceMetadataCollectionFactory.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Resource\\\\Factory\\\\TemplatesDirResourceMetadataCollectionFactory\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Resource/Factory/TemplatesDirResourceMetadataCollectionFactory.php - message: "#^Class Sylius\\\\Resource\\\\Metadata\\\\Resource\\\\ResourceClassList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" count: 1 path: src/Component/src/Metadata/Resource/ResourceClassList.php - message: "#^Class Sylius\\\\Resource\\\\Metadata\\\\Resource\\\\ResourceMetadataCollection extends generic class ArrayObject but does not specify its types\\: TKey, TValue$#" count: 1 path: src/Component/src/Metadata/Resource/ResourceMetadataCollection.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:__construct\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:__construct\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:__construct\\(\\) has parameter \\$operations with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:getDenormalizationContext\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:getNormalizationContext\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:getValidationContext\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:getVars\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:withDenormalizationContext\\(\\) has parameter \\$denormalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:withNormalizationContext\\(\\) has parameter \\$normalizationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:withValidationContext\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\ResourceMetadata\\:\\:withVars\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/ResourceMetadata.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Show\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Show.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Show\\:\\:__construct\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Show.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Show\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Show.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Show\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Show.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Show\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Show.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Show\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Show.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Update\\:\\:__construct\\(\\) has parameter \\$formOptions with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Update.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Update\\:\\:__construct\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Update.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Update\\:\\:__construct\\(\\) has parameter \\$redirectArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Update.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Update\\:\\:__construct\\(\\) has parameter \\$repositoryArguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Update.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Update\\:\\:__construct\\(\\) has parameter \\$routeRequirements with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Update.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Update\\:\\:__construct\\(\\) has parameter \\$validationContext with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Update.php - message: "#^Method Sylius\\\\Resource\\\\Metadata\\\\Update\\:\\:__construct\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Metadata/Update.php - message: "#^Method Sylius\\\\Resource\\\\Model\\\\ResourceInterface\\:\\:getId\\(\\) has no return type specified\\.$#" count: 1 path: src/Component/src/Model/ResourceInterface.php - message: "#^Class Sylius\\\\Resource\\\\Model\\\\ResourceLogEntry extends generic class Gedmo\\\\Loggable\\\\Entity\\\\MappedSuperclass\\\\AbstractLogEntry but does not specify its types\\: T$#" count: 1 path: src/Component/src/Model/ResourceLogEntry.php - message: "#^Method Sylius\\\\Resource\\\\Model\\\\TimestampableInterface\\:\\:setCreatedAt\\(\\) has no return type specified\\.$#" count: 1 path: src/Component/src/Model/TimestampableInterface.php - message: "#^Method Sylius\\\\Resource\\\\Model\\\\TimestampableInterface\\:\\:setUpdatedAt\\(\\) has no return type specified\\.$#" count: 1 path: src/Component/src/Model/TimestampableInterface.php - message: "#^Method Sylius\\\\Resource\\\\Reflection\\\\ClassReflection\\:\\:getClassAttributes\\(\\) return type with generic class ReflectionAttribute does not specify its types\\: T$#" count: 1 path: src/Component/src/Reflection/ClassReflection.php - message: "#^Method Sylius\\\\Resource\\\\Reflection\\\\ClassReflection\\:\\:getResourcesByPath\\(\\) return type has no value type specified in iterable type iterable\\.$#" count: 1 path: src/Component/src/Reflection/ClassReflection.php - message: "#^Method Sylius\\\\Resource\\\\Reflection\\\\ClassReflection\\:\\:getResourcesByPaths\\(\\) has parameter \\$paths with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Reflection/ClassReflection.php - message: "#^Method Sylius\\\\Resource\\\\Reflection\\\\Filter\\\\FunctionArgumentsFilter\\:\\:filter\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Reflection/Filter/FunctionArgumentsFilter.php - message: "#^Method Sylius\\\\Resource\\\\Reflection\\\\Filter\\\\FunctionArgumentsFilter\\:\\:filter\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Reflection/Filter/FunctionArgumentsFilter.php - message: "#^Method Sylius\\\\Resource\\\\Reflection\\\\Filter\\\\FunctionArgumentsFilter\\:\\:getFunctionArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Reflection/Filter/FunctionArgumentsFilter.php - message: "#^Method Sylius\\\\Resource\\\\State\\\\Factory\\:\\:parseArgumentValues\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/State/Factory.php - message: "#^Method Sylius\\\\Resource\\\\State\\\\Factory\\:\\:parseArgumentValues\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/State/Factory.php - message: "#^Method Sylius\\\\Resource\\\\State\\\\Provider\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/State/Provider.php - message: "#^Method Sylius\\\\Resource\\\\State\\\\Provider\\\\FactoryProvider\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/State/Provider/FactoryProvider.php - message: "#^Method Sylius\\\\Resource\\\\State\\\\Provider\\\\ReadProvider\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/State/Provider/ReadProvider.php - message: "#^Method Sylius\\\\Resource\\\\State\\\\Provider\\\\SecurityProvider\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/State/Provider/SecurityProvider.php - message: "#^Method Sylius\\\\Resource\\\\State\\\\ProviderInterface\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/State/ProviderInterface.php - message: "#^Method Sylius\\\\Resource\\\\Storage\\\\StorageInterface\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Storage/StorageInterface.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Controller\\\\MainController\\:\\:__invoke\\(\\) should return Symfony\\\\Component\\\\HttpFoundation\\\\Response but returns mixed\\.$#" count: 1 path: src/Component/src/Symfony/Controller/MainController.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\EventDispatcher\\\\GenericEvent\\:\\:getMessageParameters\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/EventDispatcher/GenericEvent.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\EventDispatcher\\\\GenericEvent\\:\\:setMessageParameters\\(\\) has parameter \\$messageParameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/EventDispatcher/GenericEvent.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\EventDispatcher\\\\GenericEvent\\:\\:stop\\(\\) has no return type specified\\.$#" count: 1 path: src/Component/src/Symfony/EventDispatcher/GenericEvent.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\EventDispatcher\\\\GenericEvent\\:\\:stop\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/EventDispatcher/GenericEvent.php - message: "#^Property Sylius\\\\Resource\\\\Symfony\\\\EventDispatcher\\\\GenericEvent\\:\\:\\$messageParameters type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/EventDispatcher/GenericEvent.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\EventDispatcher\\\\State\\\\DispatchPostReadEventProvider\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/EventDispatcher/State/DispatchPostReadEventProvider.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\EventListener\\\\AddFormatListener\\:\\:getNotAcceptableHttpException\\(\\) has parameter \\$mimeTypes with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/EventListener/AddFormatListener.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\ArgumentParser\\:\\:__construct\\(\\) has parameter \\$providers with no value type specified in iterable type iterable\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/ArgumentParser.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\ArgumentParser\\:\\:parseExpression\\(\\) has parameter \\$variables with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/ArgumentParser.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\ArgumentParserInterface\\:\\:parseExpression\\(\\) has parameter \\$variables with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/ArgumentParserInterface.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\RequestVariables\\:\\:getVariables\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/RequestVariables.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\SyliusRepositoriesVariables\\:\\:getVariables\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/SyliusRepositoriesVariables.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\TokenVariables\\:\\:getVariables\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/TokenVariables.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\VariablesCollection\\:\\:getVariables\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/VariablesCollection.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\VariablesCollectionInterface\\:\\:getVariables\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/VariablesCollectionInterface.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\VariablesInterface\\:\\:getVariables\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/VariablesInterface.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\VarsResolver\\:\\:resolve\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/VarsResolver.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\VarsResolver\\:\\:resolve\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/VarsResolver.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\VarsResolverInterface\\:\\:resolve\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/VarsResolverInterface.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\ExpressionLanguage\\\\VarsResolverInterface\\:\\:resolve\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/ExpressionLanguage/VarsResolverInterface.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Form\\\\State\\\\FormProvider\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Form/State/FormProvider.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Request\\\\RepositoryArgumentResolver\\:\\:filterPrivateArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Request/RepositoryArgumentResolver.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Request\\\\RepositoryArgumentResolver\\:\\:getArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Request/RepositoryArgumentResolver.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Request\\\\State\\\\Provider\\:\\:parseArgumentValues\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Request/State/Provider.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Request\\\\State\\\\Provider\\:\\:parseArgumentValues\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Request/State/Provider.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Request\\\\State\\\\Provider\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Request/State/Provider.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Response\\\\ApiHeadersInitiator\\:\\:initializeHeaders\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Response/ApiHeadersInitiator.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Response\\\\HeadersInitiatorInterface\\:\\:initializeHeaders\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Response/HeadersInitiatorInterface.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Routing\\\\Factory\\\\OperationRouteFactory\\:\\:getSyliusOptions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Routing/Factory/OperationRouteFactory.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Routing\\\\RedirectHandler\\:\\:getRouteArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Routing/RedirectHandler.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Routing\\\\RedirectHandler\\:\\:parseResourceValues\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Routing/RedirectHandler.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Routing\\\\RedirectHandler\\:\\:parseResourceValues\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Routing/RedirectHandler.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Routing\\\\RedirectHandler\\:\\:redirectToRoute\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Routing/RedirectHandler.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Routing\\\\RedirectHandlerInterface\\:\\:redirectToRoute\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Routing/RedirectHandlerInterface.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Security\\\\OperationAccessChecker\\:\\:getVariables\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Security/OperationAccessChecker.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Security\\\\OperationAccessChecker\\:\\:isGranted\\(\\) has parameter \\$extraVariables with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Security/OperationAccessChecker.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Serializer\\\\State\\\\DeserializeProvider\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Serializer/State/DeserializeProvider.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Serializer\\\\State\\\\DeserializeProvider\\:\\:provide\\(\\) should return array\\|object\\|null but returns mixed\\.$#" count: 1 path: src/Component/src/Symfony/Serializer/State/DeserializeProvider.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Session\\\\Flash\\\\FlashHelper\\:\\:getTranslationParameters\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Session/Flash/FlashHelper.php - message: "#^Method Sylius\\\\Resource\\\\Symfony\\\\Validator\\\\State\\\\ValidateProvider\\:\\:provide\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Validator/State/ValidateProvider.php - message: "#^PHPDoc tag @var for variable \\$data has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Symfony/Validator/State/ValidateProvider.php - message: "#^Method Sylius\\\\Resource\\\\Translation\\\\Provider\\\\ImmutableTranslationLocaleProvider\\:\\:__construct\\(\\) has parameter \\$definedLocalesCodes with no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Translation/Provider/ImmutableTranslationLocaleProvider.php - message: "#^Method Sylius\\\\Resource\\\\Twig\\\\Context\\\\Factory\\\\ContextFactory\\:\\:create\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Twig/Context/Factory/ContextFactory.php - message: "#^Method Sylius\\\\Resource\\\\Twig\\\\Context\\\\Factory\\\\ContextFactoryInterface\\:\\:create\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Twig/Context/Factory/ContextFactoryInterface.php - message: "#^Method Sylius\\\\Resource\\\\Twig\\\\Context\\\\Factory\\\\DefaultContextFactory\\:\\:create\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Twig/Context/Factory/DefaultContextFactory.php - message: "#^Method Sylius\\\\Resource\\\\Twig\\\\Context\\\\Factory\\\\RequestContextFactory\\:\\:create\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Component/src/Twig/Context/Factory/RequestContextFactory.php ================================================ FILE: phpstan.neon ================================================ includes: - phpstan-baseline.neon - vendor/phpstan/phpstan-webmozart-assert/extension.neon - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/rules.neon parameters: level: max reportUnmatchedIgnoredErrors: false paths: - 'src/' excludePaths: # External vendor dependencies installed via composer in Component # These are third-party packages that shouldn't be analyzed - 'src/Component/vendor/' # PHPSpec tests use a different syntax and structure than regular PHP code # They contain specs with methods like it_*() and shouldReturn() that PHPStan doesn't understand - 'src/Bundle/spec/' - 'src/Component/spec/' # PHPUnit tests - test code is not production code and has different quality requirements # Tests often use mocks, stubs and test-specific patterns - 'src/Bundle/Tests/' - 'src/Component/tests/' # MongoDB ODM integration - requires doctrine/mongodb-odm which is optional # These files contain references to MongoDB classes that don't exist without the package - 'src/Bundle/Doctrine/ODM/' - 'src/Bundle/EventListener/ODM*' - 'src/Bundle/DependencyInjection/Driver/Doctrine/DoctrineODMDriver.php' # Legacy code maintained for backward compatibility # Contains deprecated interfaces and implementations that will be removed in 2.0 - 'src/Component/legacy/' # Parameters.php extends Symfony's ParameterBag but changes return type covariance # This is a known limitation that cannot be fixed without breaking BC # PHPStan reports: "Return type mixed of method Parameters::get() is not covariant with # return type mixed of method ParameterBag::get()" - 'src/Bundle/Controller/Parameters.php' # ResourceMetadata will need to use the generic T - 'src/Component/src/Metadata/ResourceMetadata.php' ignoreErrors: # Optional MongoDB ODM support - requires doctrine/mongodb-odm-bundle package # These classes are only loaded when MongoDB driver is configured and the bundle is installed # See: src/Bundle/AbstractResourceBundle.php:112 - '/Class Doctrine\\Bundle\\MongoDBBundle\\DependencyInjection\\Compiler\\DoctrineMongoDBMappingsPass not found/' - '/Class Doctrine\\ODM\\MongoDB\\DocumentManager not found/' - '/has invalid type Doctrine\\MongoDB\\Query\\Builder/' # Optional PHPCR ODM support - requires doctrine/phpcr-bundle package # PHPCR is deprecated since 1.3 and will be removed in 2.0 # See: src/Bundle/AbstractResourceBundle.php:120-127 - '/Class Doctrine\\Bundle\\PHPCRBundle\\DependencyInjection\\Compiler\\DoctrinePhpcrMappingsPass not found/' - '/Class Doctrine\\ODM\\PHPCR\\Document\\Resource not found/' - '/has invalid type Doctrine\\ODM\\PHPCR\\Query\\Builder\\QueryBuilder/' - '/has invalid type Doctrine\\ODM\\PHPCR\\DocumentManagerInterface/' # Optional HateoasBundle support - requires willdurand/hateoas-bundle - '/Call to method createRepresentation\(\) on an unknown class Hateoas\\Representation\\Factory\\PagerfantaFactory/' - '/Class Hateoas\\Representation\\Factory\\PagerfantaFactory not found/' - '/Instantiated class Hateoas\\Configuration\\Route not found/' - '/Parameter \$pagerfantaRepresentationFactory of method Sylius\\Bundle\\ResourceBundle\\Controller\\ResourcesCollectionProvider::__construct\(\) has invalid type Hateoas\\Representation\\Factory\\PagerfantaFactory/' - '/Property Sylius\\Bundle\\ResourceBundle\\Controller\\ResourcesCollectionProvider::\$pagerfantaRepresentationFactory has unknown class Hateoas\\Representation\\Factory\\PagerfantaFactory as its type/' # Optional FOS RestBundle support - requires friendsofsymfony/rest-bundle - '/Call to method getContext\(\) on an unknown class FOS\\RestBundle\\View\\View/' - '/Call to method handle\(\) on an unknown class FOS\\RestBundle\\View\\ConfigurableViewHandlerInterface/' - '/Call to method setExclusionStrategyGroups\(\) on an unknown class FOS\\RestBundle\\View\\ConfigurableViewHandlerInterface/' - '/Call to method setExclusionStrategyVersion\(\) on an unknown class FOS\\RestBundle\\View\\ConfigurableViewHandlerInterface/' - '/Parameter \$restViewHandler of method Sylius\\Bundle\\ResourceBundle\\Controller\\ViewHandler::__construct\(\) has invalid type FOS\\RestBundle\\View\\ConfigurableViewHandlerInterface/' - '/Parameter \$view of method Sylius\\Bundle\\ResourceBundle\\Controller\\ViewHandler::handle\(\) has invalid type FOS\\RestBundle\\View\\View/' - '/Parameter \$view of method Sylius\\Bundle\\ResourceBundle\\Controller\\ViewHandlerInterface::handle\(\) has invalid type FOS\\RestBundle\\View\\View/' - '/Property Sylius\\Bundle\\ResourceBundle\\Controller\\ViewHandler::\$restViewHandler has unknown class FOS\\RestBundle\\View\\ConfigurableViewHandlerInterface as its type/' # Optional Winzou StateMachine support - requires winzou/state-machine package - '/Access to an undefined property Sylius\\Resource\\StateMachine\\StateMachine::\$config/' - '/Call to an undefined method Sylius\\Resource\\StateMachine\\StateMachine::getPossibleTransitions\(\)/' - '/Call to method get\(\) on an unknown class SM\\Factory\\Factory/' - '/Class SM\\Callback\\CallbackFactoryInterface not found/' - '/Class SM\\Callback\\CascadeTransitionCallback not found/' - '/Class SM\\Factory\\FactoryInterface not found/' - '/Class winzou\\Bundle\\StateMachineBundle\\winzouStateMachineBundle not found/' - '/Method Sylius\\Resource\\Winzou\\StateMachine\\OperationStateMachine::getFactory\(\) has invalid return type SM\\Factory\\Factory/' - '/Parameter \$factory of method Sylius\\Resource\\Winzou\\StateMachine\\OperationStateMachine::__construct\(\) has invalid type SM\\Factory\\Factory/' - '/Parameter \$stateMachineFactory of method Sylius\\Bundle\\ResourceBundle\\Controller\\StateMachine::__construct\(\) has invalid type SM\\Factory\\FactoryInterface/' - '/Property Sylius\\Bundle\\ResourceBundle\\Controller\\StateMachine::\$stateMachineFactory has unknown class SM\\Factory\\FactoryInterface as its type/' - '/Property Sylius\\Resource\\Winzou\\StateMachine\\OperationStateMachine::\$factory has unknown class SM\\Factory\\Factory as its type/' # Optional Symfony Workflow support - requires symfony/workflow - '/Call to method get\(\) on an unknown class \Symfony\\Component\\Workflow\\Registry/' - '/Method Sylius\\Resource\\Symfony\\Workflow\\OperationStateMachine::getRegistry\(\) has invalid return type Symfony\\Component\\Workflow\\Registry/' - '/Parameter \$registry of method Sylius\\Bundle\\ResourceBundle\\Controller\\Workflow::__construct\(\) has invalid type Symfony\\Component\\Workflow\\Registry/' - '/Parameter \$registry of method Sylius\\Resource\\Symfony\\Workflow\\OperationStateMachine::__construct\(\) has invalid type Symfony\\Component\\Workflow\\Registry/' - '/Property Sylius\\Bundle\\ResourceBundle\\Controller\\Workflow::\$registry has unknown class Symfony\\Component\\Workflow\\Registry as its type/' - '/Property Sylius\\Resource\\Symfony\\Workflow\\OperationStateMachine::\$registry has unknown class Symfony\\Component\\Workflow\\Registry as its type/' # Backward compatibility with Doctrine Common 2.x # Namespace moved from Doctrine\Common\Persistence to Doctrine\Persistence in 3.x # Code uses both namespaces with aliases to support both versions # See: src/Bundle/DependencyInjection/Driver/Doctrine/AbstractDoctrineDriver.php:46 - '/Class Doctrine\\Common\\Persistence\\ObjectManager not found/' # Doctrine ORM 3.x introduces new mapping classes # AssociationMapping class exists only in ORM 3.x, not in ORM 2.x # Code supports both versions, so we need to ignore these errors when running with ORM 2.x # See: src/Bundle/EventListener/ORMMappedSuperClassSubscriber.php - '/Class Doctrine\\ORM\\Mapping\\AssociationMapping not found/' - '/Call to method .* on an unknown class Doctrine\\ORM\\Mapping\\AssociationMapping/' - '/PHPDoc tag @var for variable .* contains unknown class Doctrine\\ORM\\Mapping\\AssociationMapping/' # Optional symfony/web-link component - only used when installed # Code checks class_exists() before using these classes # See: src/Bundle/Controller/ControllerTrait.php:443-448 - '/has invalid type Psr\\Link\\LinkInterface/' - '/Class Symfony\\Component\\WebLink\\GenericLinkProvider not found/' - '/Instantiated class Symfony\\Component\\WebLink\\GenericLinkProvider not found/' # Backward compatibility with deprecated Symfony ExpressionLanguage cache interfaces # ParserCacheInterface was removed in Symfony 4.0, replaced by PSR-6 CacheItemPoolInterface # Code provides BC layer with trigger_deprecation # See: src/Bundle/ExpressionLanguage/ExpressionLanguage.php:29-40 - '/Class Symfony\\Component\\ExpressionLanguage\\ParserCache\\ParserCacheInterface not found/' - '/Instantiated class Symfony\\Component\\ExpressionLanguage\\ParserCache\\ParserCacheAdapter not found/' # Symfony Config Component uses fluent interface with dynamic return types # getRootNode() returns NodeDefinition which gets transformed to ArrayNodeDefinition # when children() is called. PHPStan cannot track these runtime type transformations # See: src/Bundle/DependencyInjection/Configuration.php:30-37 - '/Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::(end|variableNode|scalarNode)\(\)/' # Symfony Dependency Injection Component can use ReflectionClass type as second argument instead of Reflector one. - '/Parameter #2 \$configurator of method Symfony\\Component\\DependencyInjection\\ContainerBuilder::registerAttributeForAutoconfiguration\(\)/' # Backward compatibility for Symfony DependencyInjection Alias::setDeprecated() # Method signature changed between Symfony versions: # - Symfony < 5.1: setDeprecated($status, $message) # - Symfony >= 5.1: setDeprecated($package, $version, $message) # Code dynamically detects parameter count and calls appropriately # See: src/Bundle/DependencyInjection/Compiler/PagerfantaBridgePass.php:62-73 - '/Method Symfony\\Component\\DependencyInjection\\Alias::setDeprecated\(\) invoked with 2 parameters, 3 required/' - '/Parameter #1 \$package of method Symfony\\Component\\DependencyInjection\\Alias::setDeprecated\(\) expects string, true given/' # ResourceControllerEvent uses if(false) for IDE support - provides class alias hint # This is intentional dead code to help IDEs understand the class_alias in GenericEvent.php # See: src/Bundle/Event/ResourceControllerEvent.php:18-22 - message: '#If condition is always false#' path: src/Bundle/Event/ResourceControllerEvent.php # Symfony <7.4 - TreeBuilder and ArrayNodeDefinition are not generic in Symfony < 7.4 # but are generic in Symfony 7.4+. We use generics for better type safety in newer versions. - message: '#^PHPDoc tag .+ is not generic\.$#' identifier: generics.notGeneric count: 11 path: src/Bundle/DependencyInjection/Configuration.php - message: '#^PHPDoc tag .+ is not generic\.$#' identifier: generics.notGeneric count: 5 path: src/Bundle/DependencyInjection/PagerfantaConfiguration.php - message: '#^PHPDoc tag .+ is not generic\.$#' identifier: generics.notGeneric count: 3 path: src/Bundle/Routing/Configuration.php # CachedTrait returns a mixed type - message: '/Method Sylius\\Resource\\Metadata\\Resource\\Factory\\CachedResourceClassListFactory::create\(\) should return Sylius\\Resource\\Metadata\\Resource\\ResourceClassList but returns mixed/' identifier: return.type count: 1 path: src/Component/src/Metadata/Resource/Factory/CachedResourceClassListFactory.php ================================================ FILE: phpunit.xml.dist ================================================ ./tests/ ./src/Component/legacy/tests/ ./src/Component/tests/ ================================================ FILE: psalm.xml ================================================ ================================================ FILE: rector.php ================================================ parameters(); $parameters->set(Option::AUTO_IMPORT_NAMES, true); $parameters->set(Option::IMPORT_SHORT_CLASSES, false); $services = $containerConfigurator->services(); $services->set(TypedPropertyRector::class); }; ================================================ FILE: src/Bundle/.gitignore ================================================ /test/app/cache /test/app/db.sql /test/app/logs /test/config/db.sql /test/var /test/vendor/ ================================================ FILE: src/Bundle/AbstractResourceBundle.php ================================================ getModelNamespace()) { foreach ($this->getSupportedDrivers() as $driver) { [$compilerPassClassName, $compilerPassMethod] = $this->getMappingCompilerPassInfo($driver); if (class_exists($compilerPassClassName)) { if (!method_exists($compilerPassClassName, $compilerPassMethod)) { throw new InvalidConfigurationException( "The 'mappingFormat' value is invalid, must be 'xml', 'yaml' or 'annotation'.", ); } switch ($this->mappingFormat) { case ResourceBundleInterface::MAPPING_XML: case ResourceBundleInterface::MAPPING_YAML: $container->addCompilerPass($compilerPassClassName::$compilerPassMethod( [$this->getConfigFilesPath() => $this->getModelNamespace()], [$this->getObjectManagerParameter()], sprintf('%s.driver.%s', $this->getBundlePrefix(), $driver), )); break; case ResourceBundleInterface::MAPPING_ANNOTATION: $container->addCompilerPass($compilerPassClassName::$compilerPassMethod( [$this->getModelNamespace()], [$this->getConfigFilesPath()], [sprintf('%s.object_manager', $this->getBundlePrefix())], sprintf('%s.driver.%s', $this->getBundlePrefix(), $driver), )); break; } } } } } /** * Return the prefix of the bundle. */ protected function getBundlePrefix(): string { return Container::underscore(substr((string) strrchr(static::class, '\\'), 1, -6)); } /** * Return the directory where are stored the doctrine mapping. */ protected function getDoctrineMappingDirectory(): string { return 'model'; } /** * Return the entity namespace. */ protected function getModelNamespace(): ?string { return (new \ReflectionClass($this))->getNamespaceName() . '\\Model'; } /** * Return mapping compiler pass class depending on driver. * * * * @throws UnknownDriverException */ protected function getMappingCompilerPassInfo(string $driverType): array { switch ($driverType) { case SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM: trigger_deprecation( 'sylius/resource-bundle', '1.3', 'The "%s" driver is deprecated. Doctrine MongoDB and PHPCR will no longer be supported in 2.0.', SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM, ); $mappingsPassClassname = DoctrineMongoDBMappingsPass::class; break; case SyliusResourceBundle::DRIVER_DOCTRINE_ORM: $mappingsPassClassname = DoctrineOrmMappingsPass::class; break; case SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM: trigger_deprecation( 'sylius/resource-bundle', '1.3', 'The "%s" driver is deprecated. Doctrine MongoDB and PHPCR will no longer be supported in 2.0.', SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM, ); $mappingsPassClassname = DoctrinePhpcrMappingsPass::class; break; default: throw new UnknownDriverException($driverType); } $compilerPassMethod = sprintf('create%sMappingDriver', ucfirst($this->mappingFormat)); return [$mappingsPassClassname, $compilerPassMethod]; } /** * Return the absolute path where are stored the doctrine mapping. */ protected function getConfigFilesPath(): string { return sprintf( '%s/Resources/config/doctrine/%s', $this->getPath(), strtolower($this->getDoctrineMappingDirectory()), ); } protected function getObjectManagerParameter(): string { return sprintf('%s.object_manager', $this->getBundlePrefix()); } } ================================================ FILE: src/Bundle/Command/DebugResourceCommand.php ================================================ setName('sylius:debug:resource'); $this->setDescription('Debug resource metadata.'); $this->setHelp( <<<'EOT' List or show resource metadata. To list run the command without an argument: $ php %command.full_name% To show the metadata for a resource, pass its alias: $ php %command.full_name% sylius.user EOT ); $this->addArgument('resource', InputArgument::OPTIONAL, 'Resource to debug'); $this->addArgument('operation', InputArgument::OPTIONAL, 'Operation to debug'); $this->addOption('legacy', null, InputOption::VALUE_NONE, 'Show legacy resource metadata.'); } public function execute(InputInterface $input, OutputInterface $output): int { /** @var string|null $resource */ $resource = $input->getArgument('resource'); $io = new SymfonyStyle($input, $output); $dumper = new Dumper($output); if (null === $resource) { $this->listResources($io); return Command::SUCCESS; } if (str_contains($resource, '.')) { $metadata = $this->registry->get($resource); } else { $metadata = $this->registry->getByClass($resource); } $resourceMetadataCollection = $this->getResourceMetadataCollection($metadata); /** @var string|null $operationName */ $operationName = $input->getArgument('operation'); if (null !== $operationName) { $operation = $resourceMetadataCollection->getOperation($metadata->getAlias(), $operationName); $this->debugOperation($operation, $io, $dumper); return Command::SUCCESS; } if ($input->getOption('legacy')) { $this->debugLegacyResourceMetadata($metadata, $input, $io, $dumper); return Command::SUCCESS; } $this->debugResource($metadata, $input, $io, $dumper); return Command::SUCCESS; } private function listResources(SymfonyStyle $io): void { /** @var iterable $resources */ $resources = $this->registry->getAll(); $resources = is_array($resources) ? $resources : iterator_to_array($resources); ksort($resources); $rows = []; foreach ($resources as $resource) { $rows[] = [$resource->getAlias()]; } $io->table(['Alias'], $rows); } private function debugResource(MetadataInterface $metadata, InputInterface $input, SymfonyStyle $io, Dumper $dumper): void { $resourceMetadataCollection = $this->getResourceMetadataCollection($metadata); $this->debugResourceMetadata($resourceMetadataCollection, $io, $dumper); $this->debugResourceCollectionOperation($metadata, $input, $io, $dumper); } private function debugLegacyResourceMetadata( MetadataInterface $metadata, InputInterface $input, SymfonyStyle $io, Dumper $dumper, ): void { $io->section('Configuration'); $values = $this->configurationToArray($metadata); $rows = []; foreach ($values as $key => $value) { $rows[] = [$key, $dumper($value)]; } $io->table(['Option', 'Value'], $rows); } private function getResourceMetadataCollection(MetadataInterface $resourceConfiguration): ResourceMetadataCollection { return $this->resourceMetadataCollectionFactory->create($resourceConfiguration->getClass('model')); } private function debugOperation(Operation $operation, SymfonyStyle $io, Dumper $dumper): void { $io->section('Operation Metadata'); $values = $this->operationToArray($operation); $rows = []; foreach ($values as $key => $value) { $rows[] = [$key, $dumper($value)]; } $io->table(['Option', 'Value'], $rows); } private function debugResourceMetadata(ResourceMetadataCollection $resourceMetadataCollection, SymfonyStyle $io, Dumper $dumper): void { $io->section('Resource Metadata'); if (0 === $resourceMetadataCollection->count()) { $io->info('This resource has no metadata.'); return; } /** @var ResourceMetadata $resourceMetadata */ foreach ($resourceMetadataCollection as $resourceMetadata) { $rows = []; $values = $this->resourceToArray($resourceMetadata); foreach ($values as $key => $value) { $rows[] = [$key, $dumper($value)]; } $io->table(['Option', 'Value'], $rows); } } private function debugResourceCollectionOperation(MetadataInterface $metadata, InputInterface $input, SymfonyStyle $io, Dumper $dumper): void { $io->section('Operations'); $resourceMetadataCollection = $this->resourceMetadataCollectionFactory->create($metadata->getClass('model')); $rows = []; /** @var ResourceMetadata $resourceMetadata */ foreach ($resourceMetadataCollection as $resourceMetadata) { $rows = $this->addResourceOperationsRows($resourceMetadata, $rows, $input); } if ($rows === []) { $io->info('This resource has no defined operations.'); return; } $io->table(['Name', 'Details'], $rows); } private function addResourceOperationsRows(ResourceMetadata $resourceMetadata, array $rows, InputInterface $input): array { /** @var string $resourceName */ $resourceName = $input->getArgument('resource'); /** @var Operation $operation */ foreach ($resourceMetadata->getOperations() ?? new Operations() as $operation) { $rows[] = [ $operation->getName(), sprintf( 'bin/console %s %s %s', $this->getName() ?? '', $resourceName, $operation->getName() ?? '', ), ]; } return $rows; } private function configurationToArray(MetadataInterface $metadata): array { $values = $this->objectToArray($metadata); $values = array_merge($values, $values['parameters']); unset($values['parameters']); return $values; } private function resourceToArray(ResourceMetadata $resource): array { $values = $this->objectToArray($resource); unset($values['operations']); return $values; } private function operationToArray(Operation $operation): array { return $this->objectToArray($operation); } private function objectToArray(object $object): array { $accessor = PropertyAccess::createPropertyAccessor(); $reflection = new \ReflectionClass($object); $values = []; foreach ($reflection->getProperties() as $property) { $propertyName = $property->getName(); if ($accessor->isReadable($object, $propertyName)) { $values[$property->getName()] = $accessor->getValue($object, $propertyName); } } return $values; } } ================================================ FILE: src/Bundle/Context/Initiator/LegacyRequestContextInitiator.php ================================================ decorated->initializeContext($request); if ([] === $attributes = $request->attributes->all('_sylius')) { return $context; } /** @var string|class-string|null $resource */ $resource = $attributes['resource'] ?? null; if (null === $resource) { return $context; } if (str_contains($resource, '.')) { $metadata = $this->resourceRegistry->get($resource); } else { $metadata = $this->resourceRegistry->getByClass($resource); } $configuration = $this->requestConfigurationFactory->create($metadata, $request); $configurationVars = $this->resolveVars($configuration->getVars()); $configuration->getParameters()->set('vars', $configurationVars); return $context->with(new MetadataOption($metadata), new RequestConfigurationOption($configuration)); } private function resolveVars(array $vars): array { if (null === $this->varsResolver) { return $vars; } return $this->varsResolver->resolve($vars); } } ================================================ FILE: src/Bundle/Context/Option/RequestConfigurationOption.php ================================================ requestConfiguration; } } ================================================ FILE: src/Bundle/Controller/AuthorizationCheckerInterface.php ================================================ attributes->has($key)) { return $request->attributes->get($key, $default); } if ($request->query->has($key)) { return $request->query->all()[$key]; } if ($request->request->has($key)) { return $request->request->all()[$key]; } return $default; } } ================================================ FILE: src/Bundle/Controller/ContainerAwareTrait.php ================================================ container = $container; } } ================================================ FILE: src/Bundle/Controller/ControllerTrait.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Sylius\Bundle\ResourceBundle\Controller; use Doctrine\Persistence\ManagerRegistry; use Psr\Container\ContainerInterface; use Psr\Link\LinkInterface; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpFoundation\Session\FlashBagAwareSessionInterface; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Stamp\StampInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\WebLink\EventListener\AddLinkHeaderListener; use Symfony\Component\WebLink\GenericLinkProvider; /** * Common features needed in controllers. * * @author Fabien Potencier * * @internal * * @property ContainerInterface $container */ trait ControllerTrait { /** * Returns true if the service id is defined. * * @final */ protected function has(string $id): bool { return $this->container->has($id); } /** * Gets a container service by its id. * * @return object The service * * @final */ protected function get(string $id) { return $this->container->get($id); } /** * Generates a URL from the given parameters. * * @see UrlGeneratorInterface * * @final */ protected function generateUrl(string $route, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string { return $this->container->get('router')->generate($route, $parameters, $referenceType); } /** * Forwards the request to another controller. * * @param string $controller The controller name (a string like Bundle\BlogBundle\Controller\PostController::indexAction) * * @final */ protected function forward(string $controller, array $path = [], array $query = []): Response { $request = $this->container->get('request_stack')->getCurrentRequest(); $path['_controller'] = $controller; $subRequest = $request->duplicate($query, null, $path); return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); } /** * Returns a RedirectResponse to the given URL. * * @final */ protected function redirect(string $url, int $status = 302): RedirectResponse { return new RedirectResponse($url, $status); } /** * Returns a RedirectResponse to the given route with the given parameters. * * @final */ protected function redirectToRoute(string $route, array $parameters = [], int $status = 302): RedirectResponse { return $this->redirect($this->generateUrl($route, $parameters), $status); } /** * Returns a JsonResponse that uses the serializer component if enabled, or json_encode. * * @final */ protected function json($data, int $status = 200, array $headers = [], array $context = []): JsonResponse { if ($this->container->has('serializer')) { $json = $this->container->get('serializer')->serialize($data, 'json', array_merge([ 'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS, ], $context)); return new JsonResponse($json, $status, $headers, true); } return new JsonResponse($data, $status, $headers); } /** * Returns a BinaryFileResponse object with original or customized file name and disposition header. * * @param \SplFileInfo|string $file File object or path to file to be sent as response * * @final */ protected function file($file, ?string $fileName = null, string $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT): BinaryFileResponse { $response = new BinaryFileResponse($file); $response->setContentDisposition($disposition, null === $fileName ? $response->getFile()->getFilename() : $fileName); return $response; } /** * Adds a flash message to the current session for type. * * @throws \LogicException * * @final */ protected function addFlash(string $type, $message) { try { $session = $this->container->get('request_stack')->getSession(); } catch (SessionNotFoundException $e) { throw new \LogicException('You cannot use the addFlash method if sessions are disabled. Enable them in "config/packages/framework.yaml".', 0, $e); } if (!$session instanceof FlashBagAwareSessionInterface) { trigger_deprecation('symfony/framework-bundle', '6.2', 'Calling "addFlash()" method when the session does not implement %s is deprecated.', FlashBagAwareSessionInterface::class); } $session->getFlashBag()->add($type, $message); } /** * Checks if the attributes are granted against the current authentication token and optionally supplied subject. * * @throws \LogicException * * @final */ protected function isGranted($attributes, $subject = null): bool { if (!$this->container->has('security.authorization_checker')) { throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".'); } return $this->container->get('security.authorization_checker')->isGranted($attributes, $subject); } /** * Throws an exception unless the attributes are granted against the current authentication token and optionally * supplied subject. * * @throws AccessDeniedException * * @final */ protected function denyAccessUnlessGranted($attributes, $subject = null, string $message = 'Access Denied.') { if (!$this->isGranted($attributes, $subject)) { $exception = $this->createAccessDeniedException($message); $exception->setAttributes($attributes); $exception->setSubject($subject); throw $exception; } } /** * Returns a rendered view. * * @final */ protected function renderView(string $view, array $parameters = []): string { if ($this->container->has('templating')) { @trigger_error('Using the "templating" service is deprecated since Symfony 4.3 and will be removed in 5.0; use Twig instead.', \E_USER_DEPRECATED); return $this->container->get('templating')->render($view, $parameters); } if (!$this->container->has('twig')) { throw new \LogicException('You can not use the "renderView" method if the Templating Component or the Twig Bundle are not available. Try running "composer require symfony/twig-bundle".'); } return $this->container->get('twig')->render($view, $parameters); } /** * Renders a view. * * @final */ protected function render( string $view, array $parameters = [], ?Response $response = null, ?int $responseCode = null ): Response { if ($this->container->has('templating')) { @trigger_error('Using the "templating" service is deprecated since Symfony 4.3 and will be removed in 5.0; use Twig instead.', \E_USER_DEPRECATED); $content = $this->container->get('templating')->render($view, $parameters); } elseif ($this->container->has('twig')) { $content = $this->container->get('twig')->render($view, $parameters); } else { throw new \LogicException('You can not use the "render" method if the Templating Component or the Twig Bundle are not available. Try running "composer require symfony/twig-bundle".'); } if (null === $response) { $response = new Response(); } $response->setContent($content); if ($responseCode !== null) { $response->setStatusCode($responseCode); } return $response; } /** * Streams a view. * * @final */ protected function stream(string $view, array $parameters = [], ?StreamedResponse $response = null): StreamedResponse { if ($this->container->has('templating')) { @trigger_error('Using the "templating" service is deprecated since Symfony 4.3 and will be removed in 5.0; use Twig instead.', \E_USER_DEPRECATED); $templating = $this->container->get('templating'); $callback = function () use ($templating, $view, $parameters) { $templating->stream($view, $parameters); }; } elseif ($this->container->has('twig')) { $twig = $this->container->get('twig'); $callback = function () use ($twig, $view, $parameters) { $twig->display($view, $parameters); }; } else { throw new \LogicException('You can not use the "stream" method if the Templating Component or the Twig Bundle are not available. Try running "composer require symfony/twig-bundle".'); } if (null === $response) { return new StreamedResponse($callback); } $response->setCallback($callback); return $response; } /** * Returns a NotFoundHttpException. * * This will result in a 404 response code. Usage example: * * throw $this->createNotFoundException('Page not found!'); * * @final */ protected function createNotFoundException(string $message = 'Not Found', ?\Throwable $previous = null): NotFoundHttpException { return new NotFoundHttpException($message, $previous); } /** * Returns an AccessDeniedException. * * This will result in a 403 response code. Usage example: * * throw $this->createAccessDeniedException('Unable to access this page!'); * * @throws \LogicException If the Security component is not available * * @final */ protected function createAccessDeniedException(string $message = 'Access Denied.', ?\Throwable $previous = null): AccessDeniedException { if (!class_exists(AccessDeniedException::class)) { throw new \LogicException('You can not use the "createAccessDeniedException" method if the Security component is not available. Try running "composer require symfony/security-bundle".'); } return new AccessDeniedException($message, $previous); } /** * Creates and returns a Form instance from the type of the form. * * @final */ protected function createForm(string $type, $data = null, array $options = []): FormInterface { return $this->container->get('form.factory')->create($type, $data, $options); } /** * Creates and returns a form builder instance. * * @final */ protected function createFormBuilder($data = null, array $options = []): FormBuilderInterface { return $this->container->get('form.factory')->createBuilder(FormType::class, $data, $options); } /** * Shortcut to return the Doctrine Registry service. * * @return ManagerRegistry * * @throws \LogicException If DoctrineBundle is not available * * @final */ protected function getDoctrine() { if (!$this->container->has('doctrine')) { throw new \LogicException('The DoctrineBundle is not registered in your application. Try running "composer require symfony/orm-pack".'); } return $this->container->get('doctrine'); } /** * Get a user from the Security Token Storage. * * @return UserInterface|object|null * * @throws \LogicException If SecurityBundle is not available * * @see TokenInterface::getUser() * * @final */ protected function getUser() { if (!$this->container->has('security.token_storage')) { throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".'); } if (null === $token = $this->container->get('security.token_storage')->getToken()) { return null; } if (!\is_object($user = $token->getUser())) { // e.g. anonymous authentication return null; } return $user; } /** * Checks the validity of a CSRF token. * * @param string $id The id used when generating the token * @param string|null $token The actual token sent with the request that should be validated * * @final */ protected function isCsrfTokenValid(string $id, ?string $token): bool { if (!$this->container->has('security.csrf.token_manager')) { throw new \LogicException('CSRF protection is not enabled in your application. Enable it with the "csrf_protection" key in "config/packages/framework.yaml".'); } return $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($id, $token)); } /** * Dispatches a message to the bus. * * @param object|Envelope $message The message or the message pre-wrapped in an envelope * @param StampInterface[] $stamps * * @final */ protected function dispatchMessage($message, array $stamps = []): Envelope { if (!$this->container->has('messenger.default_bus')) { $message = class_exists(Envelope::class) ? 'You need to define the "messenger.default_bus" configuration option.' : 'Try running "composer require symfony/messenger".'; throw new \LogicException('The message bus is not enabled in your application. '.$message); } return $this->container->get('messenger.default_bus')->dispatch($message, $stamps); } /** * Adds a Link HTTP header to the current response. * * @see https://tools.ietf.org/html/rfc5988 * * @final */ protected function addLink(Request $request, LinkInterface $link) { if (!class_exists(AddLinkHeaderListener::class)) { throw new \LogicException('You can not use the "addLink" method if the WebLink component is not available. Try running "composer require symfony/web-link".'); } if (null === $linkProvider = $request->attributes->get('_links')) { $request->attributes->set('_links', new GenericLinkProvider([$link])); return; } $request->attributes->set('_links', $linkProvider->withLink($link)); } } ================================================ FILE: src/Bundle/Controller/DisabledAuthorizationChecker.php ================================================ eventDispatcher = $eventDispatcher; } public function dispatch( string $eventName, RequestConfiguration $requestConfiguration, ResourceInterface $resource, ): ResourceControllerEvent { $eventName = $requestConfiguration->getEvent() ?: $eventName; $metadata = $requestConfiguration->getMetadata(); $event = new ResourceControllerEvent($resource); $this->eventDispatcher->dispatch($event, sprintf('%s.%s.%s', $metadata->getApplicationName(), $metadata->getName(), $eventName)); return $event; } public function dispatchMultiple( string $eventName, RequestConfiguration $requestConfiguration, $resources, ): ResourceControllerEvent { $eventName = $requestConfiguration->getEvent() ?: $eventName; $metadata = $requestConfiguration->getMetadata(); $event = new ResourceControllerEvent($resources); $this->eventDispatcher->dispatch($event, sprintf('%s.%s.%s', $metadata->getApplicationName(), $metadata->getName(), $eventName)); return $event; } public function dispatchPreEvent( string $eventName, RequestConfiguration $requestConfiguration, ResourceInterface $resource, ): ResourceControllerEvent { $eventName = $requestConfiguration->getEvent() ?: $eventName; $metadata = $requestConfiguration->getMetadata(); $event = new ResourceControllerEvent($resource); $this->eventDispatcher->dispatch($event, sprintf('%s.%s.pre_%s', $metadata->getApplicationName(), $metadata->getName(), $eventName)); return $event; } public function dispatchPostEvent( string $eventName, RequestConfiguration $requestConfiguration, ResourceInterface $resource, ): ResourceControllerEvent { $eventName = $requestConfiguration->getEvent() ?: $eventName; $metadata = $requestConfiguration->getMetadata(); $event = new ResourceControllerEvent($resource); $this->eventDispatcher->dispatch($event, sprintf('%s.%s.post_%s', $metadata->getApplicationName(), $metadata->getName(), $eventName)); return $event; } public function dispatchInitializeEvent( string $eventName, RequestConfiguration $requestConfiguration, ResourceInterface $resource, ): ResourceControllerEvent { $eventName = $requestConfiguration->getEvent() ?: $eventName; $metadata = $requestConfiguration->getMetadata(); $event = new ResourceControllerEvent($resource); $this->eventDispatcher->dispatch( $event, sprintf('%s.%s.initialize_%s', $metadata->getApplicationName(), $metadata->getName(), $eventName), ); return $event; } } ================================================ FILE: src/Bundle/Controller/EventDispatcherInterface.php ================================================ requestStack = $requestStack; $this->translator = $translator; $this->defaultLocale = $defaultLocale; } public function addSuccessFlash( RequestConfiguration $requestConfiguration, string $actionName, ?ResourceInterface $resource = null, ): void { $this->addFlashWithType($requestConfiguration, $actionName, 'success'); } public function addErrorFlash(RequestConfiguration $requestConfiguration, string $actionName): void { $this->addFlashWithType($requestConfiguration, $actionName, 'error'); } public function addFlashFromEvent(RequestConfiguration $requestConfiguration, ResourceControllerEvent $event): void { $this->addFlash($event->getMessageType(), $event->getMessage(), $event->getMessageParameters()); } private function addFlashWithType(RequestConfiguration $requestConfiguration, string $actionName, string $type): void { $metadata = $requestConfiguration->getMetadata(); $parameters = $this->getParametersWithName($metadata, $actionName); $message = (string) $requestConfiguration->getFlashMessage($actionName); if (empty($message)) { return; } if ($this->isTranslationDefined($message, $this->defaultLocale, $parameters)) { if (!$this->translator instanceof TranslatorBagInterface) { $this->addFlash($type, $message, $parameters); return; } $this->addFlash($type, $message); return; } $this->addFlash( $type, $this->getResourceMessage($actionName), $parameters, ); } private function addFlash(string $type, string $message, array $parameters = []): void { if (!empty($parameters)) { $message = $this->prepareMessage($message, $parameters); } if ($this->requestStack instanceof SessionInterface) { $session = $this->requestStack; } else { $session = $this->requestStack->getSession(); } /** @var FlashBagInterface $flashBag */ $flashBag = $session->getBag('flashes'); $flashBag->add($type, $message); } private function prepareMessage(string $message, array $parameters): array { return [ 'message' => $message, 'parameters' => $parameters, ]; } private function getResourceMessage(string $actionName): string { return sprintf('sylius.resource.%s', $actionName); } private function isTranslationDefined(string $message, string $locale, array $parameters): bool { if ($this->translator instanceof TranslatorBagInterface) { $defaultCatalogue = $this->translator->getCatalogue($locale); return $defaultCatalogue->has($message, 'flashes'); } return $message !== $this->translator->trans($message, $parameters, 'flashes'); } private function getParametersWithName(MetadataInterface $metadata, string $actionName): array { $applicationName = $metadata->getApplicationName(); if (stripos($actionName, 'bulk') !== false) { $resourceName = $metadata->getPluralName(); $fallback = ucfirst($resourceName); return ['%resources%' => $this->translateResourceName($applicationName, $resourceName, $fallback)]; } $resourceName = $metadata->getName(); $fallback = ucfirst($metadata->getHumanizedName()); return ['%resource%' => $this->translateResourceName($applicationName, $resourceName, $fallback)]; } private function translateResourceName(string $applicationName, string $resourceName, string $fallback): string { $snakeCaseName = u($resourceName)->snake()->toString(); $translationKey = sprintf('%s.ui.%s', $applicationName, $snakeCaseName); $translated = $this->translator->trans($translationKey, [], 'messages'); return $translated === $translationKey ? $fallback : $translated; } } ================================================ FILE: src/Bundle/Controller/FlashHelperInterface.php ================================================ getFactoryMethod()) { /** @var ResourceInterface $resource */ $resource = $factory->createNew(); return $resource; } if (is_array($method) && 2 === count($method)) { $factory = $method[0]; $method = $method[1]; } $arguments = array_values($requestConfiguration->getFactoryArguments()); return $factory->$method(...$arguments); } } ================================================ FILE: src/Bundle/Controller/NewResourceFactoryInterface.php ================================================ = 6) { class Parameters extends ParameterBag { /** * @param mixed $default */ public function get(string $key, $default = null): mixed { $result = parent::get($key, $default); if (null === $result && $default !== null && $this->has($key)) { $result = $default; } return $result; } } } else { class Parameters extends ParameterBag { /** * @param mixed $default * * @return mixed */ public function get(string $key, $default = null) { $result = parent::get($key, $default); if (null === $result && $default !== null && $this->has($key)) { $result = $default; } return $result; } } } ================================================ FILE: src/Bundle/Controller/ParametersParser.php ================================================ container = $container; $this->expression = $expression; } public function parseRequestValues(array $parameters, Request $request): array { return array_map( /** * @param mixed $parameter * * @return mixed */ function ($parameter) use ($request) { if (is_array($parameter)) { return $this->parseRequestValues($parameter, $request); } return $this->parseRequestValue($parameter, $request); }, $parameters, ); } /** * @param mixed $parameter * * @return mixed */ private function parseRequestValue($parameter, Request $request) { if (!is_string($parameter)) { return $parameter; } if (0 === strpos($parameter, '$')) { return RequestParameterProvider::provide($request, substr($parameter, 1)); } if (0 === strpos($parameter, 'expr:')) { return $this->parseRequestValueExpression(substr($parameter, 5), $request); } if (0 === strpos($parameter, '!!')) { return $this->parseRequestValueTypecast($parameter, $request); } return $parameter; } /** @return mixed */ private function parseRequestValueExpression(string $expression, Request $request) { $expression = (string) preg_replace_callback( '/(\$\w+)/', /** * @return mixed */ function (array $matches) use ($request) { $variable = $this->getFromRequest($request, substr($matches[1], 1)); if (is_array($variable) || is_object($variable)) { throw new \InvalidArgumentException(sprintf( 'Cannot use %s ($%s) as parameter in expression.', gettype($variable), $matches[1], )); } return is_string($variable) ? sprintf('"%s"', addslashes($variable)) : $variable; }, $expression, ); return $this->expression->evaluate($expression, ['container' => $this->container]); } /** @return mixed */ private function parseRequestValueTypecast(string $parameter, Request $request) { [$typecast, $castedValue] = explode(' ', $parameter, 2); /** @var callable $castFunctionName */ $castFunctionName = substr($typecast, 2) . 'val'; Assert::oneOf($castFunctionName, ['intval', 'floatval', 'boolval'], 'Variable can be casted only to int, float or bool.'); return $castFunctionName($this->parseRequestValue($castedValue, $request)); } } ================================================ FILE: src/Bundle/Controller/ParametersParserInterface.php ================================================ router = $router; } public function redirectToResource(RequestConfiguration $configuration, ResourceInterface $resource): Response { try { return $this->redirectToRoute( $configuration, (string) $configuration->getRedirectRoute(ResourceActions::SHOW), $configuration->getRedirectParameters($resource), ); } catch (RouteNotFoundException $exception) { return $this->redirectToRoute( $configuration, (string) $configuration->getRedirectRoute(ResourceActions::INDEX), $configuration->getRedirectParameters($resource), ); } } public function redirectToIndex(RequestConfiguration $configuration, ?ResourceInterface $resource = null): Response { return $this->redirectToRoute( $configuration, (string) $configuration->getRedirectRoute('index'), $configuration->getRedirectParameters($resource), ); } public function redirectToRoute(RequestConfiguration $configuration, string $route, array $parameters = []): Response { if ('referer' === $route) { return $this->redirectToReferer($configuration); } return $this->redirect($configuration, $this->router->generate($route, $parameters)); } public function redirect(RequestConfiguration $configuration, string $url, int $status = 302): Response { if ($configuration->isHeaderRedirection()) { return new Response('', 200, [ 'X-SYLIUS-LOCATION' => $url . $configuration->getRedirectHash(), ]); } return new RedirectResponse($url . $configuration->getRedirectHash(), $status); } public function redirectToReferer(RequestConfiguration $configuration): Response { return $this->redirect($configuration, (string) $configuration->getRedirectReferer()); } } ================================================ FILE: src/Bundle/Controller/RedirectHandlerInterface.php ================================================ metadata = $metadata; $this->request = $request; $this->parameters = $parameters; } /** * @return Request */ public function getRequest() { return $this->request; } /** * @return MetadataInterface */ public function getMetadata() { return $this->metadata; } /** * @return Parameters */ public function getParameters() { return $this->parameters; } /** * @return string|null */ public function getSection() { return $this->parameters->get('section'); } /** * @return bool */ public function isHtmlRequest() { return 'html' === $this->request->getRequestFormat(); } /** * @param string $name * * @return string|null */ public function getDefaultTemplate($name) { $templatesNamespace = (string) $this->metadata->getTemplatesNamespace(); if (false !== strpos($templatesNamespace, ':')) { return sprintf('%s:%s.%s', $templatesNamespace, $name, 'twig'); } return sprintf('%s/%s.%s', $templatesNamespace, $name, 'twig'); } /** * @param string $name * * @return mixed */ public function getTemplate($name) { $template = $this->parameters->get('template', $this->getDefaultTemplate($name)); if (null === $template) { throw new \RuntimeException(sprintf('Could not resolve template for resource "%s".', $this->metadata->getAlias())); } return $template; } /** * @return string|null */ public function getFormType() { $form = $this->parameters->get('form'); if (isset($form['type'])) { return $form['type']; } if (is_string($form)) { return $form; } return $this->metadata->getClass('form'); } /** * @return array */ public function getFormOptions() { $form = $this->parameters->get('form'); if (isset($form['options'])) { return $form['options']; } return []; } /** * @param string $name * * @return string */ public function getRouteName($name) { $section = $this->getSection(); $sectionPrefix = $section ? $section . '_' : ''; return sprintf('%s_%s%s_%s', $this->metadata->getApplicationName(), $sectionPrefix, $this->metadata->getName(), $name); } /** * @param string $name * * @return mixed|string|null */ public function getRedirectRoute($name) { $redirect = $this->parameters->get('redirect'); if (null === $redirect) { return $this->getRouteName($name); } if (is_array($redirect)) { if (!empty($redirect['referer'])) { return 'referer'; } return $redirect['route']; } return $redirect; } /** * Get url hash fragment (#text) which is you configured. * * @return string */ public function getRedirectHash() { $redirect = $this->parameters->get('redirect'); if (!is_array($redirect) || empty($redirect['hash'])) { return ''; } return '#' . $redirect['hash']; } /** * Get redirect referer, This will detected by configuration * If not exists, The `referrer` from headers will be used. * * @return string|null */ public function getRedirectReferer() { /** @var array|null $redirect */ $redirect = $this->parameters->get('redirect'); /** @var string|null $referer */ $referer = $this->request->headers->get('referer'); if (!is_array($redirect) || empty($redirect['referer'])) { return $referer; } if ($redirect['referer'] === true) { return $referer; } return $redirect['referer']; } /** * @param object|null $resource * * @return array */ public function getRedirectParameters($resource = null) { $redirect = $this->parameters->get('redirect'); if (isset($redirect['parameters']) && $redirect['parameters'] === []) { return []; } if (!is_array($redirect)) { $redirect = ['parameters' => []]; } $parameters = $redirect['parameters'] ?? []; $parameters = $this->addExtraRedirectParameters($parameters); if (null !== $resource) { $parameters = $this->parseResourceValues($parameters, $resource); } return $parameters; } /** * @param array $parameters */ private function addExtraRedirectParameters($parameters): array { $vars = $this->getVars(); $accessor = PropertyAccess::createPropertyAccessor(); if ($accessor->isReadable($vars, '[redirect][parameters]')) { $extraParameters = $accessor->getValue($vars, '[redirect][parameters]'); if (is_array($extraParameters)) { $parameters = array_merge($parameters, $extraParameters); } } return $parameters; } /** * @return bool */ public function isLimited() { return (bool) $this->parameters->get('limit', false); } /** * @return int|null */ public function getLimit() { $limit = null; if ($this->isLimited()) { $limit = (int) $this->parameters->get('limit', 10); } return $limit; } /** * @return bool */ public function isPaginated() { $pagination = $this->parameters->get('paginate', true); return $pagination !== false && $pagination !== null; } /** * @return int */ public function getPaginationMaxPerPage() { return (int) $this->parameters->get('paginate', 10); } /** * @return bool */ public function isFilterable() { return (bool) $this->parameters->get('filterable', false); } /** * @return array */ public function getCriteria(array $criteria = []) { $defaultCriteria = array_merge($this->parameters->get('criteria', []), $criteria); if ($this->isFilterable()) { return $this->getRequestParameter('criteria', $defaultCriteria); } return $defaultCriteria; } /** * @return bool */ public function isSortable() { return (bool) $this->parameters->get('sortable', false); } /** * @return array */ public function getSorting(array $sorting = []) { $defaultSorting = array_merge($this->parameters->get('sorting', []), $sorting); if ($this->isSortable()) { $sorting = $this->getRequestParameter('sorting'); foreach ($defaultSorting as $key => $value) { if (!isset($sorting[$key])) { $sorting[$key] = $value; } } return $sorting; } return $defaultSorting; } /** * @param string $parameter * @param array $defaults * * @return array */ public function getRequestParameter($parameter, $defaults = []) { return array_replace_recursive( $defaults, RequestParameterProvider::provide($this->request, $parameter, []), ); } /** * @return array|string|null */ public function getRepositoryMethod() { if (!$this->parameters->has('repository')) { return null; } $repository = $this->parameters->get('repository'); return is_array($repository) ? $repository['method'] : $repository; } /** * @return array */ public function getRepositoryArguments() { if (!$this->parameters->has('repository')) { return []; } $repository = $this->parameters->get('repository'); if (!isset($repository['arguments'])) { return []; } return is_array($repository['arguments']) ? $repository['arguments'] : [$repository['arguments']]; } /** * @return array|string|null */ public function getFactoryMethod() { if (!$this->parameters->has('factory')) { return null; } $factory = $this->parameters->get('factory'); return is_array($factory) ? $factory['method'] : $factory; } /** * @return array */ public function getFactoryArguments() { if (!$this->parameters->has('factory')) { return []; } $factory = $this->parameters->get('factory'); if (!isset($factory['arguments'])) { return []; } return is_array($factory['arguments']) ? $factory['arguments'] : [$factory['arguments']]; } /** * @param string $message * * @return mixed|null */ public function getFlashMessage($message) { return $this->parameters->get('flash', sprintf('%s.%s.%s', $this->metadata->getApplicationName(), $this->metadata->getName(), $message)); } /** * @return mixed|null */ public function getSortablePosition() { return $this->parameters->get('sortable_position', 'position'); } /** * @return array|null */ public function getSerializationGroups() { return $this->parameters->get('serialization_groups', []); } /** * @return mixed|null */ public function getSerializationVersion() { return $this->parameters->get('serialization_version'); } /** * @return string|null */ public function getEvent() { return $this->parameters->get('event'); } /** * @return bool */ public function hasPermission() { return false !== $this->parameters->get('permission', false); } /** * @param string $name * * @return string * * @throws \LogicException */ public function getPermission($name) { $permission = $this->parameters->get('permission'); if (null === $permission) { throw new \LogicException('Current action does not require any authorization.'); } if (true === $permission) { return sprintf('%s.%s.%s', $this->metadata->getApplicationName(), $this->metadata->getName(), $name); } return $permission; } /** * @return bool */ public function isHeaderRedirection() { $redirect = $this->parameters->get('redirect'); if (!is_array($redirect) || !isset($redirect['header'])) { return false; } if ('xhr' === $redirect['header']) { return $this->getRequest()->isXmlHttpRequest(); } return (bool) $redirect['header']; } /** * @return array */ public function getVars() { return $this->parameters->get('vars', []); } /** * @param object $resource */ private function parseResourceValues(array $parameters, $resource): array { $accessor = PropertyAccess::createPropertyAccessor(); if (empty($parameters)) { return ['id' => $accessor->getValue($resource, 'id')]; } foreach ($parameters as $key => $value) { if (is_array($value)) { $parameters[$key] = $this->parseResourceValues($value, $resource); } if (is_string($value) && 0 === strpos($value, 'resource.')) { $parameters[$key] = $accessor->getValue($resource, substr($value, 9)); } } return $parameters; } /** * @return bool */ public function hasGrid() { return $this->parameters->has('grid'); } /** * @return string * * @throws \LogicException */ public function getGrid() { if (!$this->hasGrid()) { throw new \LogicException('Current action does not use grid.'); } return $this->parameters->get('grid'); } /** * @return bool */ public function hasStateMachine() { return $this->parameters->has('state_machine'); } /** * @return string|null */ public function getStateMachineGraph() { $options = $this->parameters->get('state_machine'); return $options['graph'] ?? null; } /** * @return string|null */ public function getStateMachineTransition() { $options = $this->parameters->get('state_machine'); return $options['transition'] ?? null; } /** * @return bool */ public function isCsrfProtectionEnabled() { return $this->parameters->get('csrf_protection', true); } } ================================================ FILE: src/Bundle/Controller/RequestConfigurationFactory.php ================================================ [0-9\.]+)/i'; private const API_GROUPS_REGEXP = '/(g|groups)=(?P[a-z,_\s]+)/i'; private ParametersParserInterface $parametersParser; /** * @var string * @psalm-var class-string */ private $configurationClass; private array $defaultParameters; /** * @psalm-param class-string $configurationClass */ public function __construct(ParametersParserInterface $parametersParser, string $configurationClass, array $defaultParameters = []) { $this->parametersParser = $parametersParser; $this->configurationClass = $configurationClass; $this->defaultParameters = $defaultParameters; } public function create(MetadataInterface $metadata, Request $request): RequestConfiguration { $parameters = array_merge($this->defaultParameters, $this->parseApiParameters($request)); $parameters = $this->parametersParser->parseRequestValues($parameters, $request); /** @psalm-suppress UnsafeInstantiation */ return new $this->configurationClass($metadata, $request, new Parameters($parameters)); } /** * @throws \InvalidArgumentException */ private function parseApiParameters(Request $request): array { $parameters = $request->attributes->get('_sylius', []); /** @var string[] $apiVersionHeaders */ $apiVersionHeaders = $request->headers->all(self::API_VERSION_HEADER); foreach ($apiVersionHeaders as $apiVersionHeader) { if (preg_match(self::API_VERSION_REGEXP, $apiVersionHeader, $matches)) { $parameters['serialization_version'] = $matches['version']; } } $allowedSerializationGroups = array_merge( $parameters['allowed_serialization_groups'] ?? [], $parameters['serialization_groups'] ?? [], ); /** @var string[] $apiGroupsHeaders */ $apiGroupsHeaders = $request->headers->all(self::API_GROUPS_HEADER); foreach ($apiGroupsHeaders as $apiGroupsHeader) { if (preg_match(self::API_GROUPS_REGEXP, $apiGroupsHeader, $matches)) { $parameters['serialization_groups'] = array_intersect($allowedSerializationGroups, array_map('trim', explode(',', $matches['groups']))); } } return $parameters; } } ================================================ FILE: src/Bundle/Controller/RequestConfigurationFactoryInterface.php ================================================ metadata = $metadata; $this->requestConfigurationFactory = $requestConfigurationFactory; $this->viewHandler = $viewHandler; $this->repository = $repository; $this->factory = $factory; $this->newResourceFactory = $newResourceFactory; $this->manager = $manager; $this->singleResourceProvider = $singleResourceProvider; $this->resourcesCollectionProvider = $resourcesFinder; $this->resourceFormFactory = $resourceFormFactory; $this->redirectHandler = $redirectHandler; $this->flashHelper = $flashHelper; $this->authorizationChecker = $authorizationChecker; $this->eventDispatcher = $eventDispatcher; $this->stateMachine = $stateMachine; $this->resourceUpdateHandler = $resourceUpdateHandler; $this->resourceDeleteHandler = $resourceDeleteHandler; } public function showAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::SHOW); $resource = $this->findOr404($configuration); $event = $this->eventDispatcher->dispatch(ResourceActions::SHOW, $configuration, $resource); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } if ($configuration->isHtmlRequest()) { return $this->render($configuration->getTemplate(ResourceActions::SHOW . '.html'), [ 'configuration' => $configuration, 'metadata' => $this->metadata, 'resource' => $resource, $this->metadata->getName() => $resource, ]); } return $this->createRestView($configuration, $resource); } public function indexAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::INDEX); $resources = $this->resourcesCollectionProvider->get($configuration, $this->repository); $event = $this->eventDispatcher->dispatchMultiple(ResourceActions::INDEX, $configuration, $resources); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } if ($configuration->isHtmlRequest()) { return $this->render($configuration->getTemplate(ResourceActions::INDEX . '.html'), [ 'configuration' => $configuration, 'metadata' => $this->metadata, 'resources' => $resources, $this->metadata->getPluralName() => $resources, ]); } return $this->createRestView($configuration, $resources); } public function createAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::CREATE); $newResource = $this->newResourceFactory->create($configuration, $this->factory); $form = $this->resourceFormFactory->create($configuration, $newResource); $form->handleRequest($request); if ($request->isMethod('POST') && $form->isSubmitted() && $form->isValid()) { $newResource = $form->getData(); $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $configuration, $newResource); if ($event->isStopped() && !$configuration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this->flashHelper->addFlashFromEvent($configuration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($configuration, $newResource); } if ($configuration->hasStateMachine()) { $stateMachine = $this->getStateMachine(); $stateMachine->apply($configuration, $newResource); } $this->repository->add($newResource); if ($configuration->isHtmlRequest()) { $this->flashHelper->addSuccessFlash($configuration, ResourceActions::CREATE, $newResource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $configuration, $newResource); if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, $newResource, Response::HTTP_CREATED); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($configuration, $newResource); } if ($request->isMethod('POST') && $form->isSubmitted() && !$form->isValid()) { $responseCode = Response::HTTP_UNPROCESSABLE_ENTITY; } if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, $form, Response::HTTP_BAD_REQUEST); } $initializeEvent = $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::CREATE, $configuration, $newResource); $initializeEventResponse = $initializeEvent->getResponse(); if (null !== $initializeEventResponse) { return $initializeEventResponse; } return $this->render($configuration->getTemplate(ResourceActions::CREATE . '.html'), [ 'configuration' => $configuration, 'metadata' => $this->metadata, 'resource' => $newResource, $this->metadata->getName() => $newResource, 'form' => $form->createView(), ], null, $responseCode ?? Response::HTTP_OK); } public function updateAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::UPDATE); $resource = $this->findOr404($configuration); $form = $this->resourceFormFactory->create($configuration, $resource); $form->handleRequest($request); if ( in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->isSubmitted() && $form->isValid() ) { $resource = $form->getData(); /** @var ResourceControllerEvent $event */ $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource); if ($event->isStopped() && !$configuration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this->flashHelper->addFlashFromEvent($configuration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToResource($configuration, $resource); } try { $this->resourceUpdateHandler->handle($resource, $configuration, $this->manager); } catch (UpdateHandlingException $exception) { if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, $form, $exception->getApiResponseCode()); } $this->flashHelper->addErrorFlash($configuration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($configuration); } if ($configuration->isHtmlRequest()) { $this->flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource); if (!$configuration->isHtmlRequest()) { if ($configuration->getParameters()->get('return_content', false)) { return $this->createRestView($configuration, $resource, Response::HTTP_OK); } return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($configuration, $resource); } if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->isSubmitted() && !$form->isValid()) { $responseCode = Response::HTTP_UNPROCESSABLE_ENTITY; } if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, $form, Response::HTTP_BAD_REQUEST); } $initializeEvent = $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::UPDATE, $configuration, $resource); $initializeEventResponse = $initializeEvent->getResponse(); if (null !== $initializeEventResponse) { return $initializeEventResponse; } return $this->render($configuration->getTemplate(ResourceActions::UPDATE . '.html'), [ 'configuration' => $configuration, 'metadata' => $this->metadata, 'resource' => $resource, $this->metadata->getName() => $resource, 'form' => $form->createView(), ], null, $responseCode ?? Response::HTTP_OK); } public function deleteAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::DELETE); $resource = $this->findOr404($configuration); if ($configuration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), (string) $request->request->get('_csrf_token'))) { throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.'); } $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource); if ($event->isStopped() && !$configuration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this->flashHelper->addFlashFromEvent($configuration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($configuration, $resource); } try { $this->resourceDeleteHandler->handle($resource, $this->repository); } catch (DeleteHandlingException $exception) { if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, null, $exception->getApiResponseCode()); } $this->flashHelper->addErrorFlash($configuration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($configuration); } if ($configuration->isHtmlRequest()) { $this->flashHelper->addSuccessFlash($configuration, ResourceActions::DELETE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $configuration, $resource); if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToIndex($configuration, $resource); } public function bulkDeleteAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::BULK_DELETE); $resources = $this->resourcesCollectionProvider->get($configuration, $this->repository); if ( $configuration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid(ResourceActions::BULK_DELETE, (string) $request->request->get('_csrf_token')) ) { throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.'); } $this->eventDispatcher->dispatchMultiple(ResourceActions::BULK_DELETE, $configuration, $resources); foreach ($resources as $resource) { $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource); if ($event->isStopped() && !$configuration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this->flashHelper->addFlashFromEvent($configuration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($configuration, $resource); } try { $this->resourceDeleteHandler->handle($resource, $this->repository); } catch (DeleteHandlingException $exception) { if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, null, $exception->getApiResponseCode()); } $this->flashHelper->addErrorFlash($configuration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($configuration); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $configuration, $resource); } if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT); } $this->flashHelper->addSuccessFlash($configuration, ResourceActions::BULK_DELETE); if (isset($postEvent)) { $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } } return $this->redirectHandler->redirectToIndex($configuration); } public function applyStateMachineTransitionAction(Request $request): Response { $stateMachine = $this->getStateMachine(); $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::UPDATE); $resource = $this->findOr404($configuration); if ($configuration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), $this->getFromRequest($request, '_csrf_token'))) { throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid CSRF token.'); } $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $configuration, $resource); if ($event->isStopped() && !$configuration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this->flashHelper->addFlashFromEvent($configuration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToResource($configuration, $resource); } if (!$stateMachine->can($configuration, $resource)) { throw new BadRequestHttpException(); } try { $this->resourceUpdateHandler->handle($resource, $configuration, $this->manager); } catch (UpdateHandlingException $exception) { if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, $resource, $exception->getApiResponseCode()); } $this->flashHelper->addErrorFlash($configuration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($configuration); } if ($configuration->isHtmlRequest()) { $this->flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource); if (!$configuration->isHtmlRequest()) { if ($configuration->getParameters()->get('return_content', true)) { return $this->createRestView($configuration, $resource, Response::HTTP_OK); } return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($configuration, $resource); } /** * @return mixed */ protected function getParameter(string $name) { if (!$this->container instanceof ContainerInterface) { throw new \RuntimeException(sprintf( 'Container passed to "%s" has to implements "%s".', self::class, ContainerInterface::class, )); } return $this->container->getParameter($name); } /** * @throws AccessDeniedException */ protected function isGrantedOr403(RequestConfiguration $configuration, string $permission): void { if (!$configuration->hasPermission()) { return; } $permission = $configuration->getPermission($permission); if (!$this->authorizationChecker->isGranted($configuration, $permission)) { throw new AccessDeniedException(); } } /** * @throws NotFoundHttpException */ protected function findOr404(RequestConfiguration $configuration): ResourceInterface { if (null === $resource = $this->singleResourceProvider->get($configuration, $this->repository)) { throw new NotFoundHttpException(sprintf('The "%s" has not been found', $this->metadata->getHumanizedName())); } return $resource; } /** * @param mixed $data */ protected function createRestView(RequestConfiguration $configuration, $data, ?int $statusCode = null): Response { if (!class_exists(View::class) || null === $this->viewHandler) { throw new LogicException('You can not use the "non-html" request if FriendsOfSymfony Rest Bundle is not available. Try running "composer require friendsofsymfony/rest-bundle".'); } $view = View::create($data, $statusCode); return $this->viewHandler->handle($configuration, $view); } protected function getStateMachine(): StateMachineInterface { if (null === $this->stateMachine) { throw new LogicException('You can not use the "state-machine" if Winzou State Machine Bundle is not available. Try running "composer require winzou/state-machine-bundle".'); } return $this->stateMachine; } } ================================================ FILE: src/Bundle/Controller/ResourceDeleteHandler.php ================================================ remove($resource); } } ================================================ FILE: src/Bundle/Controller/ResourceDeleteHandlerInterface.php ================================================ formFactory = $formFactory; } public function create(RequestConfiguration $requestConfiguration, ResourceInterface $resource): FormInterface { $formType = (string) $requestConfiguration->getFormType(); $formOptions = $requestConfiguration->getFormOptions(); if ($requestConfiguration->isHtmlRequest()) { return $this->formFactory->create($formType, $resource, $formOptions); } return $this->formFactory->createNamed('', $formType, $resource, array_merge($formOptions, ['csrf_protection' => false])); } } ================================================ FILE: src/Bundle/Controller/ResourceFormFactoryInterface.php ================================================ stateMachine = $stateMachine; } public function handle( ResourceInterface $resource, RequestConfiguration $requestConfiguration, ObjectManager $manager, ): void { if (null !== $this->stateMachine && $requestConfiguration->hasStateMachine()) { $this->stateMachine->apply($requestConfiguration, $resource); } $manager->flush(); } } ================================================ FILE: src/Bundle/Controller/ResourceUpdateHandlerInterface.php ================================================ resourcesResolver->getResources($requestConfiguration, $repository); $paginationLimits = []; if ($resources instanceof ResourceGridView) { $paginator = $resources->getData(); $paginationLimits = $resources->getDefinition()->getLimits(); } else { $paginator = $resources; } if ($paginator instanceof Pagerfanta) { $request = $requestConfiguration->getRequest(); $paginator->setMaxPerPage($this->resolveMaxPerPage( $request->query->has('limit') ? $request->query->getInt('limit') : null, $requestConfiguration->getPaginationMaxPerPage(), $paginationLimits, )); $currentPage = (int) $request->query->get('page', '1'); $paginator->setCurrentPage($currentPage); // This prevents Pagerfanta from querying database from a template $paginator->getCurrentPageResults(); if (!$requestConfiguration->isHtmlRequest()) { if (null === $this->pagerfantaRepresentationFactory) { throw new \LogicException('The "willdurand/hateoas-bundle" must be installed and configured to render a resource collection on non-HTML request. Try running "composer require willdurand/hateoas-bundle"'); } $route = new Route($request->attributes->get('_route'), array_merge($request->attributes->get('_route_params'), $request->query->all())); return $this->pagerfantaRepresentationFactory->createRepresentation($paginator, $route); } } return $resources; } /** * @param int[] $gridLimits */ private function resolveMaxPerPage(?int $requestLimit, int $configurationLimit, array $gridLimits = []): int { if (null === $requestLimit) { return reset($gridLimits) ?: $configurationLimit; } if (!empty($gridLimits)) { $maxGridLimit = max($gridLimits); return $requestLimit > $maxGridLimit ? $maxGridLimit : $requestLimit; } return $requestLimit; } } ================================================ FILE: src/Bundle/Controller/ResourcesCollectionProviderInterface.php ================================================ getRepositoryMethod(); if (null !== $method) { if (is_array($method) && 2 === count($method)) { $repository = $method[0]; $method = $method[1]; } $arguments = array_values($requestConfiguration->getRepositoryArguments()); return $repository->$method(...$arguments); } $criteria = []; if ($requestConfiguration->isFilterable()) { $criteria = $requestConfiguration->getCriteria(); } $sorting = []; if ($requestConfiguration->isSortable()) { $sorting = $requestConfiguration->getSorting(); } if ($requestConfiguration->isPaginated()) { return $repository->createPaginator($criteria, $sorting); } return $repository->findBy($criteria, $sorting, $requestConfiguration->getLimit()); } } ================================================ FILE: src/Bundle/Controller/ResourcesResolverInterface.php ================================================ getRepositoryMethod(); if (null !== $method) { if (is_array($method) && 2 === count($method)) { $repository = $method[0]; $method = $method[1]; } $arguments = array_values($requestConfiguration->getRepositoryArguments()); return $repository->$method(...$arguments); } $criteria = []; $request = $requestConfiguration->getRequest(); if ($request->attributes->has('id')) { /** @var ResourceInterface|null $resource */ $resource = $repository->find($request->attributes->get('id')); return $resource; } if ($request->attributes->has('slug')) { $criteria = ['slug' => $request->attributes->get('slug')]; } $criteria = array_merge($criteria, $requestConfiguration->getCriteria()); /** @var ResourceInterface|null $resource */ $resource = $repository->findOneBy($criteria); return $resource; } } ================================================ FILE: src/Bundle/Controller/SingleResourceProviderInterface.php ================================================ stateMachineFactory = $stateMachineFactory; } public function can(RequestConfiguration $configuration, ResourceInterface $resource): bool { if (!$configuration->hasStateMachine()) { throw new \InvalidArgumentException('State machine must be configured to apply transition, check your routing.'); } $graph = $configuration->getStateMachineGraph() ?? 'default'; /** @var string $transition */ $transition = $configuration->getStateMachineTransition(); return $this->stateMachineFactory->get($resource, $graph)->can($transition); } public function apply(RequestConfiguration $configuration, ResourceInterface $resource): void { if (!$configuration->hasStateMachine()) { throw new \InvalidArgumentException('State machine must be configured to apply transition, check your routing.'); } $graph = $configuration->getStateMachineGraph() ?? 'default'; /** @var string $transition */ $transition = $configuration->getStateMachineTransition(); $this->stateMachineFactory->get($resource, $graph)->apply($transition); } } ================================================ FILE: src/Bundle/Controller/StateMachineInterface.php ================================================ isHtmlRequest()) { $this->restViewHandler->setExclusionStrategyGroups($requestConfiguration->getSerializationGroups() ?? []); $version = $requestConfiguration->getSerializationVersion(); if (null !== $version) { $this->restViewHandler->setExclusionStrategyVersion($version); } $view->getContext()->enableMaxDepth(); } return $this->restViewHandler->handle($view); } } ================================================ FILE: src/Bundle/Controller/ViewHandlerInterface.php ================================================ registry = $registry; } /** * @inheritdoc */ public function can(RequestConfiguration $configuration, ResourceInterface $resource): bool { Assert::true($configuration->hasStateMachine(), 'State machine must be configured to apply transition, check your routing.'); $graph = $configuration->getStateMachineGraph(); /** @var string $transitionName */ $transitionName = $configuration->getStateMachineTransition(); return $this->registry->get($resource, $graph)->can($resource, $transitionName); } /** * @inheritdoc */ public function apply(RequestConfiguration $configuration, ResourceInterface $resource): void { Assert::true($configuration->hasStateMachine(), 'State machine must be configured to apply transition, check your routing.'); $graph = $configuration->getStateMachineGraph(); /** @var string $transitionName */ $transitionName = $configuration->getStateMachineTransition(); $this->registry->get($resource, $graph)->apply($resource, $transitionName); } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/CsrfTokenManagerPass.php ================================================ hasDefinition('security.csrf.token_manager')) { return; } $csrdTokenManagerDefinition = $container->getDefinition('security.csrf.token_manager'); $csrdTokenManagerDefinition->setPublic(true); } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/DoctrineContainerRepositoryFactoryPass.php ================================================ hasParameter(DoctrineORMDriver::GENERIC_ENTITIES_PARAMETER)) { $container->setParameter(DoctrineORMDriver::GENERIC_ENTITIES_PARAMETER, []); } } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/DoctrineTargetEntitiesResolverPass.php ================================================ targetEntitiesResolver = $targetEntitiesResolver; } public function process(ContainerBuilder $container): void { try { /** @var array $resources */ $resources = $container->getParameter('sylius.resources'); $resolveTargetEntityListener = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity'); } catch (InvalidArgumentException $exception) { return; } $interfaces = $this->targetEntitiesResolver->resolve($resources); foreach ($interfaces as $interface => $model) { $resolveTargetEntityListener->addMethodCall('addResolveTargetEntity', [$interface, $model, []]); } if (!$resolveTargetEntityListener->hasTag('doctrine.event_listener')) { $resolveTargetEntityListener->addTag('doctrine.event_listener', ['event' => 'loadClassMetadata']); $resolveTargetEntityListener->addTag('doctrine.event_listener', ['event' => 'onClassMetadataNotFound']); } } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/Helper/TargetEntitiesResolver.php ================================================ $configuration) { $model = $this->getModel($alias, $configuration); $modelInterfaces = class_implements($model) ?: []; foreach ($modelInterfaces as $interface) { if ($interface === ResourceInterface::class) { continue; } if (isset($interfaces[$interface]) && in_array($model, $interfaces[$interface], true)) { continue; } $interfaces[$interface][] = $model; } } $interfaces = array_filter($interfaces, static function (array $classes): bool { return count($classes) === 1; }); $interfaces = array_map(static function (array $classes): string { return current($classes); }, $interfaces); foreach ($resourcesConfiguration as $alias => $configuration) { if (isset($configuration['classes']['interface'])) { $model = $this->getModel($alias, $configuration); $interface = $configuration['classes']['interface']; trigger_deprecation( 'sylius/resource-bundle', '1.6', 'Specifying the interface for resources is deprecated and will be removed in 2.0. Please rely on auto-discovering interfaces instead. Triggered by resource "%s" with model "%s" and interface "%s".', $alias, $model, $interface, ); $interfaces[$interface] = $model; } } return $interfaces; } private function getModel(string $alias, array $configuration): string { if (!isset($configuration['classes']['model'])) { throw new \InvalidArgumentException(sprintf('Could not get model class from resource "%s".', $alias)); } return $configuration['classes']['model']; } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/Helper/TargetEntitiesResolverInterface.php ================================================ internalUse) { trigger_deprecation( 'sylius/resource-bundle', '1.7', 'The "%s" class is deprecated. Migrate your Pagerfanta configuration from WhiteOctoberPagerfantaBundle to BabDevPagerfantaBundle, the configuration bridge will be removed in 2.0.', self::class, ); } $this->changeViewFactoryClass($container); $this->aliasRenamedServices($container); } private function changeViewFactoryClass(ContainerBuilder $container): void { if (!$container->hasParameter('white_october_pagerfanta.view_factory.class') || !$container->hasDefinition('pagerfanta.view_factory')) { return; } /** @var string $viewFactoryClass */ $viewFactoryClass = $container->getParameter('white_october_pagerfanta.view_factory.class'); $container->getDefinition('pagerfanta.view_factory') ->setClass($viewFactoryClass) ; } private function aliasRenamedServices(ContainerBuilder $container): void { $setDeprecatedMethod = (new \ReflectionClass(Alias::class))->getMethod('setDeprecated'); if ($container->hasDefinition('pagerfanta.twig_extension')) { if (2 === $setDeprecatedMethod->getNumberOfParameters()) { $container->setAlias('twig.extension.pagerfanta', 'pagerfanta.twig_extension') ->setDeprecated(true, 'The "%alias_id%" service alias is deprecated since Sylius 1.8, use the "pagerfanta.twig_extension" service ID instead.') ; } else { $container->setAlias('twig.extension.pagerfanta', 'pagerfanta.twig_extension') ->setDeprecated('sylius/resource-bundle', '1.8', 'The "%alias_id%" service alias is deprecated since Sylius 1.8, use the "pagerfanta.twig_extension" service ID instead.') ; } } if ($container->hasDefinition('pagerfanta.view_factory')) { if (2 === $setDeprecatedMethod->getNumberOfParameters()) { $container->setAlias('white_october_pagerfanta.view_factory', 'pagerfanta.view_factory') ->setDeprecated(true, 'The "%alias_id%" service alias is deprecated since Sylius 1.8, use the "pagerfanta.view_factory" service ID instead.') ; } else { $container->setAlias('white_october_pagerfanta.view_factory', 'pagerfanta.view_factory') ->setDeprecated('sylius/resource-bundle', '1.8', 'The "%alias_id%" service alias is deprecated since Sylius 1.8, use the "pagerfanta.view_factory" service ID instead.') ; } } } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/PrioritizedCompositeServicePass.php ================================================ serviceId = $serviceId; $this->compositeId = $compositeId; $this->tagName = $tagName; $this->methodName = $methodName; } public function process(ContainerBuilder $container): void { if (!$container->has($this->compositeId)) { return; } $this->injectTaggedServicesIntoComposite($container); $this->addAliasForCompositeIfServiceDoesNotExist($container); } private function injectTaggedServicesIntoComposite(ContainerBuilder $container): void { $contextDefinition = $container->findDefinition($this->compositeId); $taggedServices = $container->findTaggedServiceIds($this->tagName); foreach ($taggedServices as $id => $tags) { $this->addMethodCalls($contextDefinition, $id, $tags); } } private function addAliasForCompositeIfServiceDoesNotExist(ContainerBuilder $container): void { if ($container->has($this->serviceId)) { return; } $container->setAlias($this->serviceId, $this->compositeId)->setPublic(true); } private function addMethodCalls(Definition $contextDefinition, string $id, array $tags): void { foreach ($tags as $attributes) { $this->addMethodCall($contextDefinition, $id, $attributes); } } private function addMethodCall(Definition $contextDefinition, string $id, array $attributes): void { $contextDefinition->addMethodCall($this->methodName, [new Reference($id), $attributes['priority'] ?? 0]); } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/RegisterFormBuilderPass.php ================================================ hasDefinition('sylius.registry.form_builder')) { return; } $registry = $container->findDefinition('sylius.registry.form_builder'); foreach ($container->findTaggedServiceIds('sylius.default_resource_form.builder') as $id => $attributes) { foreach ($attributes as $attribute) { if (!isset($attribute['type'])) { throw new \InvalidArgumentException('Tagged grid drivers needs to have "type" attribute.'); } $registry->addMethodCall('register', [$attribute['type'], new Reference($id)]); } } } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/RegisterFqcnControllersPass.php ================================================ getParameter('sylius.resources'); } catch (InvalidArgumentException $exception) { return; } foreach ($resources as $alias => $configuration) { $metadata = Metadata::fromAliasAndConfiguration($alias, $configuration); if (!$metadata->hasClass('controller')) { continue; } $this->validateSyliusResource($metadata->getClass('model')); $controllerFqcn = $metadata->getClass('controller'); if ($controllerFqcn !== ResourceController::class) { $definition = $container->getDefinition($metadata->getServiceId('controller')); // TODO: Change to alias definition after bumping to > Symfony 5.1 $container->setDefinition($metadata->getClass('controller'), $definition); } } } private function validateSyliusResource(string $class): void { if (!in_array(ResourceInterface::class, class_implements($class) ?: [], true)) { throw new InvalidArgumentException(sprintf( 'Class "%s" must implement "%s" to be registered as a Sylius resource.', $class, ResourceInterface::class, )); } } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/RegisterResourceRepositoryPass.php ================================================ hasParameter('sylius.resources') || !$container->has('sylius.registry.resource_repository')) { return; } /** @var array $resources */ $resources = $container->getParameter('sylius.resources'); $repositoryRegistry = $container->findDefinition('sylius.registry.resource_repository'); foreach ($resources as $alias => $configuration) { [$applicationName, $resourceName] = explode('.', $alias, 2); $repositoryId = sprintf('%s.repository.%s', $applicationName, $resourceName); if ($container->has($repositoryId)) { $repositoryRegistry->addMethodCall('register', [$alias, new Reference($repositoryId)]); } } } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/RegisterResourceStateMachinePass.php ================================================ hasParameter('sylius.resources')) { return; } /** @var array $resources */ $resources = $container->getParameter('sylius.resources'); foreach ($resources as $alias => $configuration) { [$applicationName, $resourceName] = explode('.', $alias, 2); $stateMachineId = sprintf('%s.controller_state_machine.%s', $applicationName, $resourceName); $stateMachineComponent = $configuration['state_machine_component'] ?? null; if (null === $stateMachineComponent) { $container->setAlias($stateMachineId, 'sylius.resource_controller.state_machine'); continue; } $specificStateMachineId = sprintf('sylius.resource_controller.state_machine.%s', $stateMachineComponent); if (!$container->hasDefinition($specificStateMachineId)) { throw new \LogicException(sprintf('State machine "%s" is not available.', $stateMachineComponent)); } $container->setAlias($stateMachineId, $specificStateMachineId); } } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/RegisterResourcesPass.php ================================================ getParameter('sylius.resources'); $registry = $container->findDefinition('sylius.resource_registry'); } catch (InvalidArgumentException $exception) { return; } foreach ($resources as $alias => $configuration) { $this->validateSyliusResource($configuration['classes']['model']); $registry->addMethodCall('addFromAliasAndConfiguration', [$alias, $configuration]); } } private function validateSyliusResource(string $class): void { /** @var array $interfaces */ $interfaces = class_implements($class); if (!in_array(ResourceInterface::class, $interfaces, true)) { throw new InvalidArgumentException(sprintf( 'Class "%s" must implement "%s" to be registered as a Sylius resource.', $class, ResourceInterface::class, )); } } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/RegisterStateMachinePass.php ================================================ getParameter('sylius.resource.settings'); $stateMachine = $settings['state_machine_component']; $container->setParameter('sylius.state_machine_component.default', null); $this->registerWinzouStateMachine($container); $this->registerSymfonyWorkflowStateMachine($container); $this->registerWinzouStateMachine($container); $this->registerSymfonyWorkflowStateMachine($container); if (null !== $stateMachine) { $this->setStateMachine($container, $stateMachine); return; } // No state machine enabled if ( !$this->isSymfonyWorkflowEnabled($container) && !$this->isWinzouStateMachineEnabled($container) ) { return; } if ($this->isWinzouStateMachineEnabled($container)) { $this->setStateMachine($container, ResourceBundleInterface::STATE_MACHINE_WINZOU); return; } $this->setStateMachine($container, ResourceBundleInterface::STATE_MACHINE_SYMFONY); } private function setStateMachine(ContainerBuilder $container, string $stateMachine): void { if (ResourceBundleInterface::STATE_MACHINE_SYMFONY === $stateMachine) { $this->setSymfonyWorkflowAsStateMachine($container); return; } if (ResourceBundleInterface::STATE_MACHINE_WINZOU === $stateMachine) { $this->setWinzouAsStateMachine($container); return; } } private function setWinzouAsStateMachine(ContainerBuilder $container): void { if (!$this->isWinzouStateMachineEnabled($container)) { throw new \LogicException('You can not use "Winzou" for your state machine if it is not available. Try running "composer require winzou/state-machine-bundle".'); } $container->setParameter('sylius.state_machine_component.default', 'winzou'); $stateMachineDefinition = $container->register('sylius.resource_controller.state_machine', StateMachine::class); $stateMachineDefinition->setPublic(false); $stateMachineDefinition->addArgument(new Reference('sm.factory')); $container->setAlias('sylius.state_machine.operation.default', 'sylius.state_machine.operation.winzou'); } private function registerWinzouStateMachine(ContainerBuilder $container): void { if (!$this->isWinzouStateMachineEnabled($container)) { return; } $stateMachineDefinition = $container->register('sylius.resource_controller.state_machine.winzou', StateMachine::class); $stateMachineDefinition->setPublic(false); $stateMachineDefinition->addArgument(new Reference('sm.factory')); } private function registerSymfonyWorkflowStateMachine(ContainerBuilder $container): void { if (!$this->isSymfonyWorkflowEnabled($container)) { return; } $stateMachineDefinition = $container->register('sylius.resource_controller.state_machine.symfony', Workflow::class); $stateMachineDefinition->setPublic(false); $stateMachineDefinition->addArgument(new Reference('workflow.registry')); } private function setSymfonyWorkflowAsStateMachine(ContainerBuilder $container): void { if (!$this->isSymfonyWorkflowEnabled($container)) { if (class_exists(SymfonyWorkflow::class)) { throw new \LogicException('You can not use "Symfony" for your state machine if it is not enabled on framework bundle.'); } throw new \LogicException('You can not use "Symfony" for your state machine if it is not available. Try running "composer require symfony/workflow".'); } $container->setParameter('sylius.state_machine_component.default', 'symfony'); $stateMachineDefinition = $container->register('sylius.resource_controller.state_machine', Workflow::class); $stateMachineDefinition->setPublic(false); $stateMachineDefinition->addArgument(new Reference('workflow.registry')); $container->setAlias('sylius.state_machine.operation.default', 'sylius.state_machine.operation.symfony'); } private function isSymfonyWorkflowEnabled(ContainerBuilder $container): bool { return $container->hasDefinition('workflow.registry') || $container->hasAlias('workflow.registry'); } private function isWinzouStateMachineEnabled(ContainerBuilder $container): bool { /** @var array $bundles */ $bundles = $container->getParameter('kernel.bundles'); return in_array(winzouStateMachineBundle::class, $bundles, true); } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/TwigPass.php ================================================ hasDefinition('twig')) { return; } $twigDefinition = $container->getDefinition('twig'); $twigDefinition->setPublic(true); } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/UnregisterFosRestDefinitionsPass.php ================================================ getParameter('kernel.bundles'); if (class_exists(FOSRestBundle::class) && in_array(FOSRestBundle::class, $bundles, true)) { return; } $container->removeDefinition('sylius.resource_controller.view_handler'); } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/UnregisterHateoasDefinitionsPass.php ================================================ getParameter('kernel.bundles'); if (class_exists(BazingaHateoasBundle::class) && in_array(BazingaHateoasBundle::class, $bundles, true)) { return; } $container->removeDefinition('sylius.resource_controller.pagerfanta_representation_factory'); } } ================================================ FILE: src/Bundle/DependencyInjection/Compiler/WinzouStateMachinePass.php ================================================ getParameter('kernel.bundles'); $winzouStateMachineEnabled = in_array(winzouStateMachineBundle::class, $bundles, true); if (!$winzouStateMachineEnabled) { return; } if ($container->hasDefinition('sm.factory') && !$container->hasDefinition(FactoryInterface::class)) { $container->setAlias(FactoryInterface::class, 'sm.factory'); } else { $container->setAlias('sm.factory', FactoryInterface::class); } if ($container->hasDefinition('sm.callback_factory') && !$container->hasDefinition(CallbackFactoryInterface::class)) { $container->setAlias(CallbackFactoryInterface::class, 'sm.callback_factory'); } else { $container->setAlias('sm.callback_factory', CallbackFactoryInterface::class); } if ($container->hasDefinition('sm.callback.cascade_transition') && !$container->hasDefinition(CascadeTransitionCallback::class)) { $container->setAlias(CascadeTransitionCallback::class, 'sm.callback.cascade_transition'); } else { $container->setAlias('sm.callback.cascade_transition', CascadeTransitionCallback::class); } $services = [ 'sm.factory', 'sm.callback_factory', 'sm.callback.cascade_transition', FactoryInterface::class, CallbackFactoryInterface::class, CascadeTransitionCallback::class, ]; foreach ($services as $id) { if ($container->hasAlias($id)) { $container->getAlias($id)->setPublic(true); } if ($container->hasDefinition($id)) { $container->getDefinition($id)->setPublic(true); } } } } ================================================ FILE: src/Bundle/DependencyInjection/Configuration.php ================================================ */ public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('sylius_resource'); /** @var ArrayNodeDefinition> $rootNode */ $rootNode = $treeBuilder->getRootNode(); $this->addResourcesSection($rootNode); $this->addSettingsSection($rootNode); $this->addTranslationsSection($rootNode); $this->addDriversSection($rootNode); $rootNode ->children() ->arrayNode('mapping') ->addDefaultsIfNotSet() ->children() ->arrayNode('imports') ->prototype('scalar')->end() ->end() ->arrayNode('paths') ->prototype('scalar')->end() ->end() ->end() ->end() ->scalarNode('authorization_checker') ->defaultValue('sylius.resource_controller.authorization_checker.disabled') ->cannotBeEmpty() ->end() ->booleanNode('routing_path_bc_layer')->end() ->scalarNode('path_segment_name_generator') ->defaultValue('sylius.metadata.path_segment_name_generator.dash') ->info('Specify a path name generator to use.') ->end() ->end() ; return $treeBuilder; } /** * @param ArrayNodeDefinition> $node */ private function addResourcesSection(ArrayNodeDefinition $node): void { $node ->children() ->arrayNode('resources') ->useAttributeAsKey('name') ->arrayPrototype() ->children() ->scalarNode('driver')->defaultValue(SyliusResourceBundle::DRIVER_DOCTRINE_ORM)->end() ->variableNode('options') ->setDeprecated('sylius/resource-bundle', '1.12', 'The "%node%" node at "%path%" is deprecated and will be removed in 2.0.') ->end() ->scalarNode('templates')->cannotBeEmpty()->end() ->scalarNode('state_machine_component')->defaultNull()->end() ->arrayNode('classes') ->isRequired() ->addDefaultsIfNotSet() ->children() ->scalarNode('model')->isRequired()->cannotBeEmpty()->end() ->scalarNode('interface')->cannotBeEmpty()->end() ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end() ->scalarNode('repository')->cannotBeEmpty()->end() ->scalarNode('factory')->defaultValue(Factory::class)->end() ->scalarNode('form')->defaultValue(DefaultResourceType::class)->cannotBeEmpty()->end() ->end() ->end() ->arrayNode('translation') ->children() ->variableNode('options') ->setDeprecated('sylius/resource-bundle', '1.12', 'The "%node%" node at "%path%" is deprecated and will be removed in 2.0.') ->end() ->arrayNode('classes') ->isRequired() ->addDefaultsIfNotSet() ->children() ->scalarNode('model')->isRequired()->cannotBeEmpty()->end() ->scalarNode('interface')->cannotBeEmpty()->end() ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end() ->scalarNode('repository')->cannotBeEmpty()->end() ->scalarNode('factory')->defaultValue(Factory::class)->end() ->scalarNode('form')->defaultValue(DefaultResourceType::class)->cannotBeEmpty()->end() ->end() ->end() ->end() ->end() ->end() ->end() ->end() ->end() ; } /** * @param ArrayNodeDefinition> $node */ private function addSettingsSection(ArrayNodeDefinition $node): void { $node ->children() ->arrayNode('settings') ->addDefaultsIfNotSet() ->children() ->variableNode('paginate')->defaultNull()->end() ->variableNode('limit')->defaultNull()->end() ->arrayNode('allowed_paginate') ->integerPrototype()->end() ->defaultValue([10, 20, 30]) ->end() ->integerNode('default_page_size')->defaultValue(10)->end() ->scalarNode('default_templates_dir')->defaultNull()->end() ->booleanNode('sortable')->defaultFalse()->end() ->variableNode('sorting')->defaultNull()->end() ->booleanNode('filterable')->defaultFalse()->end() ->variableNode('criteria')->defaultNull()->end() ->scalarNode('state_machine_component')->defaultNull()->end() ->end() ->end() ->end() ; } /** * @param ArrayNodeDefinition> $node */ private function addTranslationsSection(ArrayNodeDefinition $node): void { $node ->children() ->arrayNode('translation') ->canBeDisabled() ->children() ->scalarNode('locale_provider')->defaultValue('sylius.translation_locale_provider.immutable')->cannotBeEmpty()->end() ->end() ->end() ; } /** * @param ArrayNodeDefinition> $node */ private function addDriversSection(ArrayNodeDefinition $node): void { $node ->children() ->arrayNode('drivers') ->defaultValue([]) ->enumPrototype()->values(SyliusResourceBundle::getAvailableDrivers())->end() ->end() ->end() ; } } ================================================ FILE: src/Bundle/DependencyInjection/Driver/AbstractDriver.php ================================================ setClassesParameters($container, $metadata); if ($metadata->hasClass('controller')) { $this->addController($container, $metadata); } $this->addManager($container, $metadata); $this->addRepository($container, $metadata); if ($metadata->hasClass('factory')) { $this->addFactory($container, $metadata); } } protected function setClassesParameters(ContainerBuilder $container, MetadataInterface $metadata): void { if ($metadata->hasClass('model')) { $container->setParameter(sprintf('%s.model.%s.class', $metadata->getApplicationName(), $metadata->getName()), $metadata->getClass('model')); } if ($metadata->hasClass('controller')) { $container->setParameter(sprintf('%s.controller.%s.class', $metadata->getApplicationName(), $metadata->getName()), $metadata->getClass('controller')); } if ($metadata->hasClass('factory')) { $container->setParameter(sprintf('%s.factory.%s.class', $metadata->getApplicationName(), $metadata->getName()), $metadata->getClass('factory')); } if ($metadata->hasClass('repository')) { $container->setParameter(sprintf('%s.repository.%s.class', $metadata->getApplicationName(), $metadata->getName()), $metadata->getClass('repository')); } if ($metadata->hasClass('form')) { $container->setParameter(sprintf('%s.form.%s.class', $metadata->getApplicationName(), $metadata->getName()), $metadata->getClass('form')); } } protected function addController(ContainerBuilder $container, MetadataInterface $metadata): void { $definition = new Definition($metadata->getClass('controller')); $definition ->setPublic(true) ->setArguments([ $this->getMetadataDefinition($metadata), new Reference('sylius.resource_controller.request_configuration_factory'), new Reference('sylius.resource_controller.view_handler', ContainerInterface::NULL_ON_INVALID_REFERENCE), new Reference($metadata->getServiceId('repository')), new Reference($metadata->getServiceId('factory')), new Reference('sylius.resource_controller.new_resource_factory'), new Reference($metadata->getServiceId('manager')), new Reference('sylius.resource_controller.single_resource_provider'), new Reference('sylius.resource_controller.resources_collection_provider'), new Reference('sylius.resource_controller.form_factory'), new Reference('sylius.resource_controller.redirect_handler'), new Reference('sylius.resource_controller.flash_helper'), new Reference('sylius.resource_controller.authorization_checker'), new Reference('sylius.resource_controller.event_dispatcher'), new Reference($metadata->getServiceId('controller_state_machine'), ContainerInterface::NULL_ON_INVALID_REFERENCE), new Reference('sylius.resource_controller.resource_update_handler'), new Reference('sylius.resource_controller.resource_delete_handler'), ]) ->addMethodCall('setContainer', [new Reference('service_container')]) ->addTag('controller.service_arguments') ; $container->setDefinition($metadata->getServiceId('controller'), $definition); } protected function addFactory(ContainerBuilder $container, MetadataInterface $metadata): void { $factoryClass = $metadata->getClass('factory'); $modelClass = $metadata->getClass('model'); $definition = new Definition($factoryClass); $definition->setPublic(true); $definitionArgs = [$modelClass]; /** @var array $factoryInterfaces */ $factoryInterfaces = class_implements($factoryClass); if (in_array(TranslatableFactoryInterface::class, $factoryInterfaces, true)) { $decoratedDefinition = new Definition(Factory::class); $decoratedDefinition->setArguments($definitionArgs); $definitionArgs = [$decoratedDefinition, new Reference('sylius.translation_locale_provider')]; } $definition->setArguments($definitionArgs); $container->setDefinition($metadata->getServiceId('factory'), $definition) ->addTag('sylius.resource_factory') ; /** @var array $factoryParents */ $factoryParents = class_parents($factoryClass); $typehintClasses = array_merge( $factoryInterfaces, [$factoryClass, LegacyFactoryInterface::class], $factoryParents, ); if (in_array(TranslatableFactoryInterface::class, $factoryInterfaces, true)) { $typehintClasses[] = LegacyTranslatableFactoryInterface::class; } foreach ($typehintClasses as $typehintClass) { $container->registerAliasForArgument( $metadata->getServiceId('factory'), $typehintClass, $metadata->getHumanizedName() . ' factory', ); } } protected function getMetadataDefinition(MetadataInterface $metadata): Definition { $definition = new Definition(Metadata::class); $definition ->setFactory([new Reference('sylius.resource_registry'), 'get']) ->setArguments([$metadata->getAlias()]) ; return $definition; } abstract protected function addManager(ContainerBuilder $container, MetadataInterface $metadata): void; abstract protected function addRepository(ContainerBuilder $container, MetadataInterface $metadata): void; } ================================================ FILE: src/Bundle/DependencyInjection/Driver/Doctrine/AbstractDoctrineDriver.php ================================================ getClassMetadataClassname()); $definition ->setFactory([new Reference($this->getManagerServiceId($metadata)), 'getClassMetadata']) ->setArguments([$metadata->getClass('model')]) ->setPublic(false) ; return $definition; } protected function addManager(ContainerBuilder $container, MetadataInterface $metadata): void { $container->setAlias( $metadata->getServiceId('manager'), new Alias($this->getManagerServiceId($metadata), true), ); foreach ([DeprecatedObjectManager::class, ObjectManager::class] as $objectManagerClass) { if (!class_exists($objectManagerClass)) { continue; } $container->registerAliasForArgument( $metadata->getServiceId('manager'), $objectManagerClass, $metadata->getHumanizedName() . ' manager', ); } } /** * Return the configured object managre name, or NULL if the default * manager should be used. */ protected function getObjectManagerName(MetadataInterface $metadata): ?string { if (!$metadata->hasParameter('options')) { return null; } /** @var string[] $options */ $options = $metadata->getParameter('options'); return $options['object_manager'] ?? null; } abstract protected function getManagerServiceId(MetadataInterface $metadata): string; abstract protected function getClassMetadataClassname(): string; } ================================================ FILE: src/Bundle/DependencyInjection/Driver/Doctrine/DoctrineODMDriver.php ================================================ getClass('model'); /** @var array $modelInterfaces */ $modelInterfaces = class_implements($modelClass); $repositoryClass = in_array(TranslatableInterface::class, $modelInterfaces) ? TranslatableRepository::class : new Parameter('sylius.mongodb.odm.repository.class') ; if ($metadata->hasClass('repository')) { $repositoryClass = $metadata->getClass('repository'); } $unitOfWorkDefinition = new Definition('Doctrine\\ODM\\MongoDB\\UnitOfWork'); $unitOfWorkDefinition ->setFactory([new Reference($this->getManagerServiceId($metadata)), 'getUnitOfWork']) ->setPublic(false) ; $definition = new Definition($repositoryClass); $definition->setArguments([ new Reference($metadata->getServiceId('manager')), $unitOfWorkDefinition, $this->getClassMetadataDefinition($metadata), ]); $definition->addTag('sylius.repository'); $container->setDefinition($metadata->getServiceId('repository'), $definition); } protected function getManagerServiceId(MetadataInterface $metadata): string { if ($objectManagerName = $this->getObjectManagerName($metadata)) { return sprintf('doctrine_mongodb.odm.%s_document_manager', $objectManagerName); } return 'doctrine_mongodb.odm.document_manager'; } protected function getClassMetadataClassname(): string { return 'Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadata'; } } ================================================ FILE: src/Bundle/DependencyInjection/Driver/Doctrine/DoctrineORMDriver.php ================================================ getApplicationName(), $metadata->getName()); $repositoryClass = EntityRepository::class; /** @var string[] $genericEntities */ $genericEntities = $container->hasParameter(self::GENERIC_ENTITIES_PARAMETER) ? $container->getParameter(self::GENERIC_ENTITIES_PARAMETER) : []; if ($container->hasParameter($repositoryClassParameterName)) { /** @var string $repositoryClass */ $repositoryClass = $container->getParameter($repositoryClassParameterName); } if ($metadata->hasClass('repository')) { /** @var string $repositoryClass */ $repositoryClass = $metadata->getClass('repository'); } $serviceId = $metadata->getServiceId('repository'); $managerReference = new Reference($metadata->getServiceId('manager')); $definition = new Definition($repositoryClass); $definition->setPublic(true); $definition->addTag('sylius.repository'); if ($repositoryClass === EntityRepository::class) { /** @var string $entityClass */ $entityClass = $metadata->getClass('model'); $definition->setFactory([$managerReference, 'getRepository']); $definition->setArguments([$entityClass]); $container->setDefinition($serviceId, $definition); $genericEntities[] = $entityClass; } else { if (is_a($repositoryClass, ServiceEntityRepository::class, true)) { $definition->setArguments([new Reference('doctrine')]); $container->setDefinition($serviceId, $definition); } else { $definition->setArguments([$managerReference, $this->getClassMetadataDefinition($metadata)]); } $container->setDefinition($serviceId, $definition); $doctrineDefinition = new Definition($repositoryClass); $doctrineDefinition->addTag(ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG); $doctrineDefinition->setFactory([new Reference('service_container'), 'get']); $doctrineDefinition->setArguments([$serviceId]); $container->setDefinition($repositoryClass, $doctrineDefinition); } /** @var array $repositoryInterfaces */ $repositoryInterfaces = class_implements($repositoryClass); /** @var array $repositoryParents */ $repositoryParents = class_parents($repositoryClass); $typehintClasses = array_merge( $repositoryInterfaces, [$repositoryClass, LegacyRepositoryInterface::class], $repositoryParents, ); foreach ($typehintClasses as $typehintClass) { $container->registerAliasForArgument( $metadata->getServiceId('repository'), $typehintClass, $metadata->getHumanizedName() . ' repository', ); } $container->setParameter(self::GENERIC_ENTITIES_PARAMETER, $genericEntities); } protected function addManager(ContainerBuilder $container, MetadataInterface $metadata): void { parent::addManager($container, $metadata); $typehintClasses = [ DeprecatedObjectManager::class, ObjectManager::class, EntityManagerInterface::class, ]; foreach ($typehintClasses as $typehintClass) { $container->registerAliasForArgument( $metadata->getServiceId('manager'), $typehintClass, $metadata->getHumanizedName() . ' manager', ); } } protected function getManagerServiceId(MetadataInterface $metadata): string { if ($objectManagerName = $this->getObjectManagerName($metadata)) { return sprintf('doctrine.orm.%s_entity_manager', $objectManagerName); } return 'doctrine.orm.entity_manager'; } protected function getClassMetadataClassname(): string { return ClassMetadata::class; } } ================================================ FILE: src/Bundle/DependencyInjection/Driver/Doctrine/DoctrinePHPCRDriver.php ================================================ addResourceListeners($container, $metadata); } protected function addResourceListeners(ContainerBuilder $container, MetadataInterface $metadata): void { $defaultOptions = [ // if no parent is given default to the parent path given here. 'parent_path_default' => null, // auto-create the parent path if it does not exist. 'parent_path_autocreate' => false, // set true to always override the parent path. 'parent_path_force' => false, // automatically replace invalid characters in the node name // with a blank space. 'name_filter' => true, // automatically resolve same-name-sibling conflicts. 'name_resolver' => true, ]; $metadataOptions = $metadata->hasParameter('options') ? $metadata->getParameter('options') : []; if ($diff = array_diff(array_keys($metadataOptions), array_keys($defaultOptions))) { throw new InvalidArgumentException(sprintf( 'Unknown PHPCR-ODM configuration options: "%s"', implode('", "', $diff), )); } $options = array_merge( $defaultOptions, $metadataOptions, ); $createEventName = sprintf('%s.%s.pre_%s', $metadata->getApplicationName(), $metadata->getName(), 'create'); $updateEventName = sprintf('%s.%s.pre_%s', $metadata->getApplicationName(), $metadata->getName(), 'update'); if ($options['parent_path_default']) { $defaultPath = new Definition(DefaultParentListener::class); $defaultPath->setArguments([ new Reference($metadata->getServiceId('manager')), $options['parent_path_default'], $options['parent_path_autocreate'], $options['parent_path_force'], ]); $defaultPath->addTag('kernel.event_listener', [ 'event' => $createEventName, 'method' => 'onPreCreate', ]); $container->setDefinition( sprintf( '%s.resource.%s.doctrine.odm.phpcr.event_listener.default_path', $metadata->getApplicationName(), $metadata->getName(), ), $defaultPath, ); } if ($options['name_filter']) { $nameFilter = new Definition(NameFilterListener::class); $nameFilter->setArguments([ new Reference($metadata->getServiceId('manager')), ]); $nameFilter->addTag('kernel.event_listener', [ 'event' => $createEventName, 'method' => 'onEvent', ]); $nameFilter->addTag('kernel.event_listener', [ 'event' => $updateEventName, 'method' => 'onEvent', ]); $container->setDefinition( sprintf( '%s.resource.%s.doctrine.odm.phpcr.event_listener.name_filter', $metadata->getApplicationName(), $metadata->getName(), ), $nameFilter, ); } if ($options['name_resolver']) { $nameResolver = new Definition(NameResolverListener::class); $nameResolver->setArguments([ new Reference($metadata->getServiceId('manager')), ]); $nameResolver->addTag('kernel.event_listener', [ 'event' => $createEventName, 'method' => 'onEvent', ]); $nameResolver->addTag('kernel.event_listener', [ 'event' => $updateEventName, 'method' => 'onEvent', ]); $container->setDefinition( sprintf( '%s.resource.%s.doctrine.odm.phpcr.event_listener.name_resolver', $metadata->getApplicationName(), $metadata->getName(), ), $nameResolver, ); } } public function getType(): string { return SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM; } protected function addRepository(ContainerBuilder $container, MetadataInterface $metadata): void { $repositoryClass = new Parameter('sylius.phpcr_odm.repository.class'); if ($metadata->hasClass('repository')) { $repositoryClass = $metadata->getClass('repository'); } $definition = new Definition($repositoryClass); $definition->setArguments([ new Reference($metadata->getServiceId('manager')), $this->getClassMetadataDefinition($metadata), ]); $definition->addTag('sylius.repository'); $container->setDefinition($metadata->getServiceId('repository'), $definition); } protected function getManagerServiceId(MetadataInterface $metadata): string { if ($objectManagerName = $this->getObjectManagerName($metadata)) { return sprintf('doctrine_phpcr.odm.%s_document_manager', $objectManagerName); } return 'doctrine_phpcr.odm.document_manager'; } protected function getClassMetadataClassname(): string { return 'Doctrine\\ODM\\PHPCR\\Mapping\\ClassMetadata'; } } ================================================ FILE: src/Bundle/DependencyInjection/Driver/DriverInterface.php ================================================ getDriver(); if (isset(self::$drivers[$type])) { return self::$drivers[$type]; } Assert::notFalse($type, sprintf('No driver was configured on the resource "%s".', $metadata->getAlias())); switch ($type) { case SyliusResourceBundle::DRIVER_DOCTRINE_ORM: return self::$drivers[$type] = new DoctrineORMDriver(); case SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM: return self::$drivers[$type] = new DoctrineODMDriver(); case SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM: return self::$drivers[$type] = new DoctrinePHPCRDriver(); } throw new UnknownDriverException($type); } } ================================================ FILE: src/Bundle/DependencyInjection/Driver/Exception/InvalidDriverException.php ================================================ setParameter(sprintf('%s.driver.%s', $this->getAlias(), $driver), true); $container->setParameter(sprintf('%s.driver', $this->getAlias()), $driver); /** @var array $resources */ $resources = $container->hasParameter('sylius.resources') ? $container->getParameter('sylius.resources') : []; foreach ($registeredResources as $resourceName => $resourceConfig) { $alias = $applicationName . '.' . $resourceName; $resourceConfig = array_merge(['driver' => $driver], $resourceConfig); $resources[$alias] = $resourceConfig; $container->setParameter('sylius.resources', $resources); $metadata = Metadata::fromAliasAndConfiguration($alias, $resourceConfig); DriverProvider::get($metadata)->load($container, $metadata); if ($metadata->hasParameter('translation')) { $alias .= '_translation'; $resourceConfig = array_merge(['driver' => $driver], $resourceConfig['translation']); $resources[$alias] = $resourceConfig; $container->setParameter('sylius.resources', $resources); $metadata = Metadata::fromAliasAndConfiguration($alias, $resourceConfig); DriverProvider::get($metadata)->load($container, $metadata); } } } } ================================================ FILE: src/Bundle/DependencyInjection/PagerfantaConfiguration.php ================================================ */ public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('white_october_pagerfanta'); /** @var ArrayNodeDefinition> $rootNode */ $rootNode = $treeBuilder->getRootNode(); $rootNode->setDeprecated('sylius/resource-bundle', '1.7', 'The "%node%" configuration node is deprecated, migrate your configuration to the "babdev_pagerfanta" configuration node.'); $this->addExceptionsStrategySection($rootNode); $rootNode ->children() ->scalarNode('default_view') ->defaultValue('default') ->end() ->end() ; return $treeBuilder; } /** * @param ArrayNodeDefinition> $node */ private function addExceptionsStrategySection(ArrayNodeDefinition $node): void { $node ->children() ->arrayNode('exceptions_strategy') ->addDefaultsIfNotSet() ->children() ->scalarNode('out_of_range_page')->defaultValue(self::EXCEPTION_STRATEGY_TO_HTTP_NOT_FOUND)->end() ->scalarNode('not_valid_current_page')->defaultValue(self::EXCEPTION_STRATEGY_TO_HTTP_NOT_FOUND)->end() ->end() ->end() ->end() ; } } ================================================ FILE: src/Bundle/DependencyInjection/PagerfantaExtension.php ================================================ internalUse) { trigger_deprecation( 'sylius/resource-bundle', '1.7', 'The "%s" class is deprecated. Migrate your Pagerfanta configuration from WhiteOctoberPagerfantaBundle to BabDevPagerfantaBundle, the configuration bridge will be removed in 2.0.', self::class, ); } $config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs); $container->setParameter('white_october_pagerfanta.default_view', $config['default_view']); } public function prepend(ContainerBuilder $container): void { $config = $this->processConfiguration($this->getConfiguration([], $container), $container->getExtensionConfig($this->getAlias())); $container->prependExtensionConfig('babdev_pagerfanta', $config); } } ================================================ FILE: src/Bundle/DependencyInjection/SyliusResourceExtension.php ================================================ processConfiguration($this->getConfiguration([], $container), $configs); $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.php'); /** @var array $bundles */ $bundles = $container->getParameter('kernel.bundles'); if (array_key_exists('SyliusGridBundle', $bundles)) { $loader->load('services/integrations/grid.php'); } if ($config['translation']['enabled']) { $loader->load('services/integrations/translation.php'); $container->setAlias('sylius.translation_locale_provider', $config['translation']['locale_provider'])->setPublic(true); } $container->setParameter('sylius.resource.mapping', $config['mapping']); $container->setParameter('sylius.resource.settings', $config['settings']); $routingPathBcLayer = $config['routing_path_bc_layer'] ?? null; if (null === $routingPathBcLayer) { $routingPathBcLayer = class_exists(Transliterator::class); } if ($routingPathBcLayer && !class_exists(Transliterator::class)) { throw new RuntimeException(sprintf('The routing path bc-layer is enabled but behat/transliterator package is not installed.')); } $container->setParameter('sylius.routing_path_bc_layer', $routingPathBcLayer); $container->setAlias('sylius.resource_controller.authorization_checker', $config['authorization_checker']); $container->setAlias('sylius.path_segment_name_generator', $config['path_segment_name_generator']); $this->registerMetadataConfiguration($container, $config); $this->autoRegisterResources($config, $container); $this->loadPersistence($config['drivers'], $config['resources'], $loader, $container); $this->loadResources($config['resources'], $container); $container->registerAttributeForAutoconfiguration(AsResource::class, static function (ChildDefinition $definition): void { $definition->addTag('container.excluded', [ 'source' => 'by #[AsResource] attribute', ]); }); $container->registerAttributeForAutoconfiguration( AsResourceMutator::class, static function (ChildDefinition $definition, AsResourceMutator $attribute, \ReflectionClass $reflector): void { if (!is_a($reflector->name, ResourceMutatorInterface::class, true)) { throw new RuntimeException(\sprintf('Resource mutator "%s" should implement %s', $reflector->name, ResourceMutatorInterface::class)); } $definition->addTag('sylius.resource_mutator', [ 'resourceClass' => $attribute->resourceClass, ]); }, ); $container->registerAttributeForAutoconfiguration( AsOperationMutator::class, static function (ChildDefinition $definition, AsOperationMutator $attribute, \ReflectionClass $reflector): void { if (!is_a($reflector->name, OperationMutatorInterface::class, true)) { throw new RuntimeException(\sprintf('Operation mutator "%s" should implement %s', $reflector->name, OperationMutatorInterface::class)); } $definition->addTag('sylius.operation_mutator', [ 'operationName' => $attribute->operationName, ]); }, ); $container->registerForAutoconfiguration(ProviderInterface::class) ->addTag('sylius.state_provider') ; $container->registerForAutoconfiguration(ProcessorInterface::class) ->addTag('sylius.state_processor') ; $container->registerForAutoconfiguration(ResponderInterface::class) ->addTag('sylius.state_responder') ; $container->registerForAutoconfiguration(FactoryInterface::class) ->addTag('sylius.resource_factory') ; $container->registerForAutoconfiguration(LegacyFactoryInterface::class) ->addTag('sylius.resource_factory') ; $container->registerForAutoconfiguration(ContextFactoryInterface::class) ->addTag('sylius.twig_context_factory') ; $container->addObjectResource(Metadata::class); $container->addObjectResource(DriverProvider::class); $container->addObjectResource(DoctrineORMDriver::class); $container->addObjectResource(DoctrineODMDriver::class); $container->addObjectResource(DoctrinePHPCRDriver::class); } public function getConfiguration(array $config, ContainerBuilder $container): Configuration { $configuration = new Configuration(); $container->addObjectResource($configuration); return $configuration; } public function prepend(ContainerBuilder $container): void { $config = ['body_listener' => ['enabled' => true]]; $container->prependExtensionConfig('fos_rest', $config); } private function autoRegisterResources(array &$config, ContainerBuilder $container): void { /** @var array $resources */ $resources = $config['resources']; /** @var array $mapping */ $mapping = $container->getParameter('sylius.resource.mapping'); $paths = $mapping['paths'] ?? []; foreach (ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories($paths) as $reflectionClass) { $className = $reflectionClass->getName(); $resourceAttributes = ClassReflection::getClassAttributes($className, AsResource::class); foreach ($resourceAttributes as $resourceAttribute) { /** @var AsResource $resource */ $resource = $resourceAttribute->newInstance(); $resourceMetadata = $resource->toMetadata(); $resourceAlias = $this->getResourceAlias($resourceMetadata, $className); if ($resources[$resourceAlias] ?? false) { continue; } $resources[$resourceAlias] = [ 'classes' => [ 'model' => $className, 'controller' => ResourceController::class, 'factory' => Factory::class, 'form' => DefaultResourceType::class, ], 'driver' => $resourceMetadata->getDriver() ?? SyliusResourceBundle::DRIVER_DOCTRINE_ORM, ]; } } $config['resources'] = $resources; } /** @param class-string $className */ private function getResourceAlias(ResourceMetadata $resource, string $className): string { $alias = $resource->getAlias(); $applicationName = $resource->getApplicationName() ?? 'app'; if (null !== $alias) { return $alias; } $reflectionClass = new \ReflectionClass($className); $shortName = $reflectionClass->getShortName(); $suffix = 'Resource'; if (str_ends_with($shortName, $suffix)) { $shortName = substr($shortName, 0, strlen($shortName) - strlen($suffix)); } return u($applicationName)->snake()->toString() . '.' . u($shortName)->snake()->toString(); } /** * @param array $resources */ private function loadPersistence(array $drivers, array $resources, LoaderInterface $loader, ContainerBuilder $container): void { $availableDrivers = $this->getAvailableDrivers($container); // Enable all available drivers if there is no configured drivers $drivers = [] !== $drivers ? $drivers : $availableDrivers; $resourceDrivers = $this->getResourceDrivers($resources); $this->checkConfiguredDrivers($drivers, $availableDrivers, $resourceDrivers); $integrateDoctrine = array_reduce($drivers, function (bool $result, string $driver): bool { return $result || in_array($driver, [SyliusResourceBundle::DRIVER_DOCTRINE_ORM, SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM, SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM], true); }, false); if ($integrateDoctrine) { $loader->load('services/integrations/doctrine.php'); } foreach ($drivers as $driver) { if (in_array($driver, [SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM, SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM], true)) { trigger_deprecation( 'sylius/resource-bundle', '1.3', 'The "%s" driver is deprecated. Doctrine MongoDB and PHPCR will no longer be supported in 2.0.', $driver, ); } // Only Doctrine drivers need service integration file if (!str_starts_with($driver, 'doctrine')) { continue; } $loader->load(sprintf('services/integrations/%s.php', $driver)); } } /** * @param array $resources * * @return array */ private function getResourceDrivers(array $resources): array { $resourceDrivers = array_map(function (array $resource): string|false { return $resource['driver'] ?? false; }, $resources); // Remove resources with disabled driver return array_filter($resourceDrivers, function (string|false $driver): bool { return false !== $driver; }); } private function getAvailableDrivers(ContainerBuilder $container): array { $availableDrivers = []; if ($container::willBeAvailable(SyliusResourceBundle::DRIVER_DOCTRINE_ORM, \Doctrine\ORM\EntityManagerInterface::class, ['doctrine/doctrine-bundle'])) { $availableDrivers[] = SyliusResourceBundle::DRIVER_DOCTRINE_ORM; } if ($container::willBeAvailable(SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM, \Doctrine\ODM\PHPCR\Document\Resource::class, ['doctrine/doctrine-bundle'])) { $availableDrivers[] = SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM; } if ($container::willBeAvailable(SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM, \Doctrine\ODM\MongoDB\DocumentManager::class, ['doctrine/doctrine-bundle'])) { $availableDrivers[] = SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM; } return $availableDrivers; } /** * @param string[] $configuredDrivers * @param string[] $availableDrivers * @param array $resourceDrivers */ private function checkConfiguredDrivers(array $configuredDrivers, array $availableDrivers, array $resourceDrivers): void { foreach ($configuredDrivers as $driver) { if (!in_array($driver, $availableDrivers, true)) { throw new InvalidArgumentException(sprintf( 'Driver "%s" is configured, but this driver is not available. Try running "composer require %s"', $driver, $driver, )); } } foreach ($resourceDrivers as $resource => $driver) { if (!in_array($driver, $availableDrivers, true)) { throw new InvalidArgumentException(sprintf( 'Resource "%s" uses drivers "%s", but this driver is not available. Try running "composer require %s"', $resource, $driver, $driver, )); } if (!in_array($driver, $configuredDrivers, true)) { throw new InvalidArgumentException(sprintf( 'Resource "%s" uses drivers "%s", but this driver is not enabled. Try adding "%s" in sylius_resource.drivers option', $resource, $driver, $driver, )); } } } private function loadResources(array $loadedResources, ContainerBuilder $container): void { /** @var array $resources */ $resources = $container->hasParameter('sylius.resources') ? $container->getParameter('sylius.resources') : []; foreach ($loadedResources as $alias => $resourceConfig) { $metadata = Metadata::fromAliasAndConfiguration($alias, $resourceConfig); $resources[$alias] = $resourceConfig; $container->setParameter('sylius.resources', $resources); if ($metadata->getDriver()) { DriverProvider::get($metadata)->load($container, $metadata); } if ($metadata->hasParameter('translation')) { $alias .= '_translation'; $resourceConfig = array_merge(['driver' => $resourceConfig['driver']], $resourceConfig['translation']); $resources[$alias] = $resourceConfig; $container->setParameter('sylius.resources', $resources); $metadata = Metadata::fromAliasAndConfiguration($alias, $resourceConfig); if ($metadata->getDriver()) { DriverProvider::get($metadata)->load($container, $metadata); } } } } private function registerMetadataConfiguration(ContainerBuilder $container, array $config): void { $resources = $this->getResourceFilesToWatch($container, $config); $container->getDefinition('sylius.metadata.resource_extractor.php_file')->replaceArgument(0, $resources); } private function getResourceFilesToWatch(ContainerBuilder $container, array $config): array { $files = []; /** @var string $path */ foreach ($config['mapping']['imports'] ?? [] as $path) { if (is_dir($path)) { foreach (Finder::create()->followLinks()->files()->in($path)->name('/\.php$/')->sortByName() as $file) { $files[] = $file->getRealPath(); } $container->addResource(new DirectoryResource($path, '/\.php$/')); continue; } if ($container->fileExists($path, false)) { if (!str_ends_with($path, '.php')) { throw new RuntimeException(\sprintf('Unsupported mapping type in "%s", supported type is PHP.', $path)); } $files[] = $path; continue; } throw new RuntimeException(\sprintf('Could not open file or directory "%s".', $path)); } return $files; } } ================================================ FILE: src/Bundle/Doctrine/ODM/MongoDB/DocumentRepository.php ================================================ getQueryBuilder() ->field('id')->equals(new \MongoId($id)) ->getQuery() ->getSingleResult() ; } public function findAll(): iterable { return $this ->getCollectionQueryBuilder() ->getQuery() ->getIterator() ; } public function findOneBy(array $criteria): object { $queryBuilder = $this->getQueryBuilder(); $this->applyCriteria($queryBuilder, $criteria); return $queryBuilder ->getQuery() ->getSingleResult() ; } /** * @param int $limit * @param int $offset */ public function findBy(array $criteria, ?array $sorting = null, $limit = null, $offset = null): iterable { $queryBuilder = $this->getCollectionQueryBuilder(); $this->applyCriteria($queryBuilder, $criteria); $this->applySorting($queryBuilder, $sorting); if (null !== $limit) { $queryBuilder->limit($limit); } if (null !== $offset) { $queryBuilder->skip($offset); } return $queryBuilder ->getQuery() ->getIterator() ; } public function createPaginator(array $criteria = [], array $sorting = []): iterable { $queryBuilder = $this->getCollectionQueryBuilder(); $this->applyCriteria($queryBuilder, $criteria); $this->applySorting($queryBuilder, $sorting); return $this->getPaginator($queryBuilder); } public function add(ResourceInterface $resource): void { $this->dm->persist($resource); $this->dm->flush(); } public function remove(ResourceInterface $resource): void { if (null !== $this->find($resource->getId())) { $this->dm->remove($resource); $this->dm->flush(); } } public function getPaginator(QueryBuilder $queryBuilder): Pagerfanta { return new Pagerfanta(new QueryAdapter($queryBuilder)); } protected function getQueryBuilder(): QueryBuilder { return $this->createQueryBuilder(); } protected function getCollectionQueryBuilder(): QueryBuilder { return $this->createQueryBuilder(); } protected function applyCriteria(QueryBuilder $queryBuilder, array $criteria = []): void { foreach ($criteria as $property => $value) { $queryBuilder->field($property)->equals($value); } } protected function applySorting(QueryBuilder $queryBuilder, array $sorting = []): void { foreach ($sorting as $property => $order) { $queryBuilder->sort($property, $order); } } } ================================================ FILE: src/Bundle/Doctrine/ODM/MongoDB/TranslatableRepository.php ================================================ $value) { if (is_array($value)) { $queryBuilder ->field($property)->in($value) ; } elseif ('' !== $value) { $queryBuilder ->field($property)->equals($value) ; } } } protected function applySorting(QueryBuilder $queryBuilder, ?array $sorting = null): void { if (null === $sorting) { return; } foreach ($sorting as $property => $order) { $queryBuilder->sort($property, $order); } } } ================================================ FILE: src/Bundle/Doctrine/ODM/PHPCR/DocumentRepository.php ================================================ getCollectionQueryBuilder(); $this->applyCriteria($queryBuilder, $criteria); $this->applySorting($queryBuilder, $sorting); return $this->getPaginator($queryBuilder); } public function add(ResourceInterface $resource): void { $this->dm->persist($resource); $this->dm->flush(); } public function remove(ResourceInterface $resource): void { if (null !== $this->find($resource->getId())) { $this->dm->remove($resource); $this->dm->flush(); } } public function getPaginator(QueryBuilder $queryBuilder): Pagerfanta { return new Pagerfanta(new QueryAdapter($queryBuilder)); } protected function getCollectionQueryBuilder(): QueryBuilder { return $this->createQueryBuilder('o'); } protected function applyCriteria(QueryBuilder $queryBuilder, array $criteria = []): void { $metadata = $this->getClassMetadata(); foreach ($criteria as $property => $value) { if (!empty($value)) { if ($property === $metadata->nodename) { $queryBuilder ->andWhere() ->eq() ->localName($this->getAlias()) ->literal($value) ; } else { $queryBuilder ->andWhere() ->eq() ->field($this->getPropertyName($property)) ->literal($value) ; } } } } protected function applySorting(QueryBuilder $queryBuilder, array $sorting = []): void { foreach ($sorting as $property => $order) { if (!empty($order)) { $queryBuilder->orderBy()->{$order}()->field('o.' . $property); } } $queryBuilder->end(); } protected function getPropertyName(string $name): string { if (false === strpos($name, '.')) { return $this->getAlias() . '.' . $name; } return $name; } protected function getAlias(): string { return 'o'; } } ================================================ FILE: src/Bundle/Doctrine/ODM/PHPCR/EventListener/DefaultParentListener.php ================================================ documentManager = $documentManager; $this->parentPath = $parentPath; $this->autocreate = $autocreate; $this->force = $force; } public function onPreCreate(ResourceControllerEvent $event) { $document = $event->getSubject(); $class = get_class($document); $this->resolveParent( $document, $this->documentManager->getClassMetadata($class), ); } private function resolveParent( $document, ClassMetadata $metadata, ) { if (!$parentField = $metadata->parentMapping) { throw new \RuntimeException(sprintf( 'A default parent path has been specified, but no parent mapping has been applied to document "%s"', get_class($document), )); } if (false === $this->force) { $actualParent = $metadata->getFieldValue($document, $parentField); if ($actualParent) { return; } } $parentDocument = $this->documentManager->find(null, $this->parentPath); if (true === $this->autocreate && null === $parentDocument) { NodeHelper::createPath($this->documentManager->getPhpcrSession(), $this->parentPath); $parentDocument = $this->documentManager->find(null, $this->parentPath); } if (null === $parentDocument) { throw new \RuntimeException(sprintf( 'Document at default parent path "%s" does not exist. `autocreate` was set to "%s"', $this->parentPath, $this->autocreate ? 'true' : 'false', )); } $metadata->setFieldValue($document, $parentField, $parentDocument); } } ================================================ FILE: src/Bundle/Doctrine/ODM/PHPCR/EventListener/NameFilterListener.php ================================================ documentManager = $documentManager; $this->replacementCharacter = $replacementCharacter; } public function onEvent(ResourceControllerEvent $event) { $document = $event->getSubject(); $metadata = $this->documentManager->getClassMetadata(get_class($document)); if (null === $nameField = $metadata->nodename) { throw new \RuntimeException(sprintf( 'In order to use the node name filter on "%s" it is necessary to map a field as the "nodename"', get_class($document), )); } $name = $metadata->getFieldValue($document, $nameField); $name = preg_replace('/\\/|:|\\[|\\]|\\||\\*/', $this->replacementCharacter, $name); $metadata->setFieldValue($document, $nameField, $name); } } ================================================ FILE: src/Bundle/Doctrine/ODM/PHPCR/EventListener/NameResolverListener.php ================================================ documentManager = $documentManager; } public function onEvent(ResourceControllerEvent $event) { $document = $event->getSubject(); $metadata = $this->documentManager->getClassMetadata(get_class($document)); if ($metadata->idGenerator !== ClassMetadata::GENERATOR_TYPE_PARENT) { throw new \RuntimeException(sprintf( 'Document of class "%s" must be using the GENERATOR_TYPE_PARENT identificatio strategy (value %s), it is current using "%s" (this may be an automatic configuration: be sure to map both the `nodename` and the `parentDocument`).', get_class($document), ClassMetadata::GENERATOR_TYPE_PARENT, $metadata->idGenerator, )); } // NOTE: that the PHPCR-ODM requires these two fields to be set when // when the GENERATOR_TYPE_PARENT "ID" strategy is used. $nameField = $metadata->nodename; $parentField = $metadata->parentMapping; $parentDocument = $metadata->getFieldValue($document, $parentField); $phpcrNode = $this->documentManager->getNodeForDocument($parentDocument); $parentPath = $phpcrNode->getPath(); $baseCandidateName = $metadata->getFieldValue($document, $nameField); $candidateName = $baseCandidateName; $index = 1; while (true) { $candidatePath = sprintf('%s/%s', $parentPath, $candidateName); $existing = $this->documentManager->find(null, $candidatePath); // if the existing document is the document we are updating, then thats great. if ($existing === $document) { return; } if (null === $existing) { $metadata->setFieldValue($document, $nameField, $candidateName); return; } $candidateName = sprintf('%s-%d', $baseCandidateName, $index); ++$index; } } } ================================================ FILE: src/Bundle/Doctrine/ODM/PHPCR/Form/Builder/DefaultFormBuilder.php ================================================ documentManager = $documentManager; } public function build(MetadataInterface $metadata, FormBuilderInterface $formBuilder, array $options): void { $classMetadata = $this->documentManager->getClassMetadata($metadata->getClass('model')); // the field mappings should only contain standard value mappings foreach ($classMetadata->fieldMappings as $fieldName) { if ($fieldName === $classMetadata->uuidFieldName) { continue; } if ($fieldName === $classMetadata->nodename) { continue; } $options = []; $mapping = $classMetadata->mappings[$fieldName]; if ($mapping['nullable'] === false) { $options['required'] = true; } $formBuilder->add($fieldName, null, $options); } } } ================================================ FILE: src/Bundle/Doctrine/ORM/ContainerRepositoryFactory.php ================================================ doctrineFactory = $doctrineFactory; $this->genericEntities = $genericEntities; } /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress InvalidReturnType */ public function getRepository(EntityManagerInterface $entityManager, $entityName): DoctrineEntityRepository { $metadata = $entityManager->getClassMetadata($entityName); if ($metadata->customRepositoryClassName === null && in_array($entityName, $this->genericEntities, true)) { /** @psalm-suppress InvalidReturnStatement */ return $this->getOrCreateRepository($entityManager, $metadata); } /** @var DoctrineEntityRepository $repository */ $repository = $this->doctrineFactory->getRepository($entityManager, $entityName); return $repository; } private function getOrCreateRepository( EntityManagerInterface $entityManager, ClassMetadata $metadata, ): DoctrineEntityRepository { $repositoryHash = $metadata->getName() . spl_object_hash($entityManager); if (!isset($this->managedRepositories[$repositoryHash])) { $this->managedRepositories[$repositoryHash] = new EntityRepository($entityManager, $metadata); } return $this->managedRepositories[$repositoryHash]; } } ================================================ FILE: src/Bundle/Doctrine/ORM/CreatePaginatorTrait.php ================================================ */ public function createPaginator(array $criteria = [], array $sorting = []): iterable { $queryBuilder = $this->createQueryBuilder('o'); $this->applyCriteria($queryBuilder, $criteria); $this->applySorting($queryBuilder, $sorting); return $this->getPaginator($queryBuilder); } protected function getPaginator(QueryBuilder $queryBuilder): PagerfantaInterface { if (!class_exists(QueryAdapter::class)) { throw new \LogicException('You can not use the "paginator" if Pargefanta Doctrine ORM Adapter is not available. Try running "composer require pagerfanta/doctrine-orm-adapter".'); } // Use output walkers option in the query adapter should be false as it affects performance greatly (see sylius/sylius#3775) return new Pagerfanta(new QueryAdapter($queryBuilder, false, false)); } /** * @param array $objects */ protected function getArrayPaginator($objects): PagerfantaInterface { return new Pagerfanta(new ArrayAdapter($objects)); } protected function applyCriteria(QueryBuilder $queryBuilder, array $criteria = []): void { foreach ($criteria as $property => $value) { if (!in_array($property, array_merge($this->getClassMetadata()->getAssociationNames(), $this->getClassMetadata()->getFieldNames()), true)) { continue; } $name = $this->getPropertyName($property); if (null === $value) { $queryBuilder->andWhere($queryBuilder->expr()->isNull($name)); } elseif (is_array($value)) { $queryBuilder->andWhere($queryBuilder->expr()->in($name, $value)); } elseif ('' !== $value) { $parameter = str_replace('.', '_', $property); $queryBuilder ->andWhere($queryBuilder->expr()->eq($name, ':' . $parameter)) ->setParameter($parameter, $value) ; } } } protected function applySorting(QueryBuilder $queryBuilder, array $sorting = []): void { foreach ($sorting as $property => $order) { if (!in_array($property, array_merge($this->getClassMetadata()->getAssociationNames(), $this->getClassMetadata()->getFieldNames()), true)) { continue; } if (!empty($order)) { $queryBuilder->addOrderBy($this->getPropertyName($property), $order); } } } protected function getPropertyName(string $name): string { if (!str_contains($name, '.')) { return 'o' . '.' . $name; } return $name; } } ================================================ FILE: src/Bundle/Doctrine/ORM/EntityRepository.php ================================================ entityManager = $entityManager; } public function build(MetadataInterface $metadata, FormBuilderInterface $formBuilder, array $options): void { $classMetadata = $this->entityManager->getClassMetadata($metadata->getClass('model')); if (1 < count($classMetadata->identifier)) { throw new \RuntimeException('The default form factory does not support entity classes with multiple primary keys.'); } $this->doBuild($classMetadata, $formBuilder); } private function doBuild(ClassMetadata $classMetadata, FormBuilderInterface $formBuilder): void { $fields = $classMetadata->fieldNames; if (!$classMetadata->isIdentifierNatural()) { $fields = array_diff($fields, $classMetadata->identifier); } foreach ($fields as $fieldName) { $options = []; // Skip fields coming from embeddables if (strpos($fieldName, '.') !== false) { continue; } if (in_array($fieldName, ['createdAt', 'updatedAt'], true)) { continue; } if (Types::DATETIME_MUTABLE === $classMetadata->getTypeOfField($fieldName)) { $options = ['widget' => 'single_text']; } $formBuilder->add($fieldName, null, $options); } foreach ($classMetadata->embeddedClasses as $fieldName => $embeddedMapping) { $nestedFormBuilder = $formBuilder->create($fieldName, null, ['data_class' => $embeddedMapping['class'], 'compound' => true]); Assert::stringNotEmpty($embeddedMapping['class']); $this->doBuild($this->entityManager->getClassMetadata($embeddedMapping['class']), $nestedFormBuilder); $formBuilder->add($nestedFormBuilder); } foreach ($classMetadata->getAssociationMappings() as $fieldName => $associationMapping) { if (ClassMetadata::ONE_TO_MANY !== $associationMapping['type']) { $formBuilder->add($fieldName, null, ['choice_label' => 'id']); } } } } ================================================ FILE: src/Bundle/Doctrine/ORM/ResourceLogEntryRepository.php ================================================ createQueryBuilder('log') ->where('log.objectId = :objectId') ->setParameter('objectId', $objectId) ; } } ================================================ FILE: src/Bundle/Doctrine/ORM/ResourceLogEntryRepositoryInterface.php ================================================ getEntityManager()->persist($resource); $this->getEntityManager()->flush(); } public function remove(ResourceInterface $resource): void { if (null !== $this->find($resource->getId())) { $this->getEntityManager()->remove($resource); $this->getEntityManager()->flush(); } } } ================================================ FILE: src/Bundle/Doctrine/ResourceMappingDriverChain.php ================================================ resourceRegistry = $resourceRegistry; $this->setDefaultDriver($mappingDriver); } public function loadMetadataForClass($className, ClassMetadata $metadata): void { parent::loadMetadataForClass($className, $metadata); $this->convertResourceMappedSuperclass($metadata); } /** * @psalm-suppress NoInterfaceProperties https://github.com/vimeo/psalm/issues/2206 */ private function convertResourceMappedSuperclass(ClassMetadata $metadata): void { if (!isset($metadata->isMappedSuperclass)) { return; } if (false === $metadata->isMappedSuperclass) { return; } try { $resourceMetadata = $this->resourceRegistry->getByClass($metadata->getName()); } catch (\InvalidArgumentException $exception) { return; } if ($metadata->getName() !== $resourceMetadata->getClass('model')) { return; } $metadata->isMappedSuperclass = false; } } ================================================ FILE: src/Bundle/Event/ResourceControllerEvent.php ================================================ resourceRegistry = $resourceRegistry; } protected function isResource(ClassMetadata $metadata): bool { return $metadata->getReflectionClass()->implementsInterface(ResourceInterface::class); } /** * @psalm-suppress InvalidReturnType */ protected function getReflectionService(): ReflectionService { if ($this->reflectionService === null) { $this->reflectionService = new RuntimeReflectionService(); } /** @psalm-suppress InvalidReturnStatement */ return $this->reflectionService; } } ================================================ FILE: src/Bundle/EventListener/AbstractDoctrineSubscriber.php ================================================ getClassMetadata(); $this->convertToDocumentIfNeeded($metadata); if (!$metadata->isMappedSuperclass) { $this->setAssociationMappings($metadata, $eventArgs->getDocumentManager()->getConfiguration()); } else { $this->unsetAssociationMappings($metadata); } } private function convertToDocumentIfNeeded(ClassMetadataInfo $metadata) { if (false === $metadata->isMappedSuperclass) { return; } try { $resourceMetadata = $this->resourceRegistry->getByClass($metadata->getName()); } catch (\InvalidArgumentException $exception) { return; } if ($metadata->getName() === $resourceMetadata->getClass('model')) { $metadata->isMappedSuperclass = false; } } /** * @param $configuration */ private function setAssociationMappings(ClassMetadataInfo $metadata, $configuration) { foreach (class_parents($metadata->getName()) as $parent) { if (false === in_array($parent, $configuration->getMetadataDriverImpl()->getAllClassNames())) { continue; } $parentMetadata = new ClassMetadata( $parent, $configuration->getNamingStrategy(), ); // Wakeup Reflection $parentMetadata->wakeupReflection($this->getReflectionService()); // Load Metadata $configuration->getMetadataDriverImpl()->loadMetadataForClass($parent, $parentMetadata); if (false === $this->isResource($parentMetadata)) { continue; } if ($parentMetadata->isMappedSuperclass) { foreach ($parentMetadata->associationMappings as $key => $value) { if ($this->isRelation($value['association']) && !isset($metadata->associationMappings[$key])) { $metadata->associationMappings[$key] = $value; } } } } } private function unsetAssociationMappings(ClassMetadataInfo $metadata) { if (false === $this->isResource($metadata)) { return; } foreach ($metadata->associationMappings as $key => $value) { if ($this->isRelation($value['association'])) { unset($metadata->associationMappings[$key]); } } } /** * @param string $type * * @return bool */ private function isRelation($type) { return in_array( $type, [ ClassMetadataInfo::REFERENCE_ONE, ClassMetadataInfo::REFERENCE_MANY, ClassMetadataInfo::EMBED_ONE, ClassMetadataInfo::EMBED_MANY, ], true, ); } } ================================================ FILE: src/Bundle/EventListener/ODMRepositoryClassSubscriber.php ================================================ setCustomRepositoryClass($eventArgs->getClassMetadata()); } private function setCustomRepositoryClass(ClassMetadata $metadata) { try { $resourceMetadata = $this->resourceRegistry->getByClass($metadata->getName()); } catch (\InvalidArgumentException $exception) { return; } if ($resourceMetadata->hasClass('repository')) { $metadata->setCustomRepositoryClass($resourceMetadata->getClass('repository')); } } } ================================================ FILE: src/Bundle/EventListener/ODMTranslatableListener.php ================================================ mappings = $mappings; $this->fallbackLocale = $fallbackLocale; } public function setCurrentLocale($currentLocale) { $this->currentLocale = $currentLocale; return $this; } public function getSubscribedEvents() { return [ Events::loadClassMetadata, Events::postLoad, ]; } /** * Add mapping to translatable entities */ public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) { $classMetadata = $eventArgs->getClassMetadata(); $reflection = $classMetadata->reflClass; if (!$reflection || $reflection->isAbstract()) { return; } if ($reflection->implementsInterface(TranslatableInterface::class)) { $this->mapTranslatable($classMetadata); } if ($reflection->implementsInterface(TranslationInterface::class)) { $this->mapTranslation($classMetadata); } } /** * Add mapping data to a translatable entity */ private function mapTranslatable(ClassMetadata $metadata) { // In the case A -> B -> TranslatableInterface, B might not have mapping defined as it // is probably defined in A, so in that case, we just return. if (!isset($this->mappings[$metadata->name])) { return; } $config = $this->mappings[$metadata->name]; $mapping = $config['translation']['mapping']; $metadata->mapManyEmbedded([ 'fieldName' => $mapping['translatable']['translations'], 'targetDocument' => $config['translation']['model'], 'strategy' => 'set', ]); } /** * Add mapping data to a translation entity */ private function mapTranslation(ClassMetadata $metadata) { // In the case A -> B -> TranslationInterface, B might not have mapping defined as it // is probably defined in A, so in that case, we just return; if (!isset($this->mappings[$metadata->name])) { return; } $config = $this->mappings[$metadata->name]; $mapping = $config['translation']['mapping']; $metadata->isEmbeddedDocument = true; $metadata->isMappedSuperclass = false; $metadata->setIdentifier(null); // Map locale field. if (!$metadata->hasField($mapping['translation']['locale'])) { $metadata->mapField([ 'fieldName' => $mapping['translation']['locale'], 'type' => 'string', ]); } // Map unique index. $keys = [ $mapping['translation']['translatable'] => 1, $mapping['translation']['locale'] => 1, ]; if (!$this->hasUniqueIndex($metadata, $keys)) { $metadata->addIndex($keys, [ 'unique' => true, ]); } } /** * Load translations */ public function postLoad(LifecycleEventArgs $args) { $document = $args->getDocument(); // Sometimes $document is a doctrine proxy class, we therefore need to retrieve it's real class $name = $args->getDocumentManager()->getClassMetadata(get_class($document))->getName(); if (!isset($this->mappings[$name])) { return; } $metadata = $this->mappings[$name]; if (isset($metadata['fallback_locale'])) { $setter = 'set' . ucfirst($metadata['fallback_locale']); $document->$setter($this->fallbackLocale); } if (isset($metadata['current_locale'])) { $setter = 'set' . ucfirst($metadata['current_locale']); $document->$setter($this->currentLocale); } } } ================================================ FILE: src/Bundle/EventListener/ORMMappedSuperClassSubscriber.php ================================================ getClassMetadata(); if (!$metadata->isMappedSuperclass) { $this->setAssociationMappings($metadata, $eventArgs->getEntityManager()->getConfiguration()); } else { $this->unsetAssociationMappings($metadata); } } private function setAssociationMappings(ClassMetadata $metadata, Configuration $configuration): void { $class = $metadata->getName(); if (!class_exists($class)) { return; } /** @psalm-suppress DeprecatedClass */ $metadataDriver = $configuration->getMetadataDriverImpl(); Assert::isInstanceOf($metadataDriver, MappingDriver::class); $parents = class_parents($class) ?: []; foreach ($parents as $parent) { if (false === in_array($parent, $metadataDriver->getAllClassNames(), true)) { continue; } $parentMetadata = new ClassMetadata( $parent, $configuration->getNamingStrategy(), ); // Wakeup Reflection /** @psalm-suppress ArgumentTypeCoercion */ $parentMetadata->wakeupReflection($this->getReflectionService()); // Load Metadata $metadataDriver->loadMetadataForClass($parent, $parentMetadata); /** @psalm-suppress InvalidArgument */ if (false === $this->isResource($parentMetadata)) { continue; } if ($parentMetadata->isMappedSuperclass) { /** * @var AssociationMapping|array{type: int} $value */ foreach ($parentMetadata->getAssociationMappings() as $key => $value) { $type = \is_array($value) ? $value['type'] : $value->type(); if ($this->isRelation($type) && !isset($metadata->associationMappings[$key])) { if (\is_array($value)) { $value['sourceEntity'] = $class; } else { /** @psalm-suppress UndefinedClass */ $value->sourceEntity = $class; /** @phpstan-ignore-line */ } $metadata->associationMappings[$key] = $value; /** @phpstan-ignore-line */ } } } } } private function unsetAssociationMappings(ClassMetadata $metadata): void { /** @psalm-suppress InvalidArgument */ if (false === $this->isResource($metadata)) { return; } /** * @var AssociationMapping|array{type: int} $value */ foreach ($metadata->getAssociationMappings() as $key => $value) { $type = \is_array($value) ? $value['type'] : $value->type(); if ($this->isRelation($type)) { unset($metadata->associationMappings[$key]); } } } private function isRelation(int $type): bool { return in_array( $type, [ ClassMetadata::MANY_TO_MANY, ClassMetadata::ONE_TO_MANY, ClassMetadata::ONE_TO_ONE, ], true, ); } } ================================================ FILE: src/Bundle/EventListener/ORMRepositoryClassSubscriber.php ================================================ setCustomRepositoryClass($eventArgs->getClassMetadata()); } private function setCustomRepositoryClass(ClassMetadata $metadata): void { try { $resourceMetadata = $this->resourceRegistry->getByClass($metadata->getName()); } catch (\InvalidArgumentException $exception) { return; } if ($resourceMetadata->hasClass('repository')) { /** @psalm-var class-string|null $repository */ $repository = $resourceMetadata->getClass('repository'); $metadata->setCustomRepositoryClass($repository); } } } ================================================ FILE: src/Bundle/EventListener/ORMTranslatableListener.php ================================================ resourceMetadataRegistry = $resourceMetadataRegistry; $this->translatableEntityLocaleAssigner = $this->processTranslatableEntityLocaleAssigner($translatableEntityLocaleAssigner); } /** * @deprecated since version 1.10, It will be removed in 2.0. */ public function getSubscribedEvents(): array { return [ Events::loadClassMetadata, Events::postLoad, ]; } /** * Add mapping to translatable entities */ public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void { $classMetadata = $eventArgs->getClassMetadata(); $reflection = $classMetadata->getReflectionClass(); /** @psalm-suppress PossiblyNullReference */ if ($reflection->isAbstract()) { return; } if ($reflection->implementsInterface(TranslatableInterface::class)) { $this->mapTranslatable($classMetadata); } if ($reflection->implementsInterface(TranslationInterface::class)) { $this->mapTranslation($classMetadata); } } public function postLoad(PostLoadEventArgs $args): void { $entity = $args->getObject(); if (!$entity instanceof TranslatableInterface) { return; } $this->translatableEntityLocaleAssigner->assignLocale($entity); } /** * Add mapping data to a translatable entity. */ private function mapTranslatable(ClassMetadata $metadata): void { $className = $metadata->name; try { $resourceMetadata = $this->resourceMetadataRegistry->getByClass($className); } catch (\InvalidArgumentException $exception) { return; } if (!$resourceMetadata->hasParameter('translation')) { return; } /** @var MetadataInterface $translationResourceMetadata */ $translationResourceMetadata = $this->resourceMetadataRegistry->get($resourceMetadata->getAlias() . '_translation'); if (!$metadata->hasAssociation('translations')) { $metadata->mapOneToMany([ 'fieldName' => 'translations', 'targetEntity' => $translationResourceMetadata->getClass('model'), 'mappedBy' => 'translatable', 'fetch' => ClassMetadata::FETCH_EXTRA_LAZY, 'indexBy' => 'locale', 'cascade' => ['persist', 'remove'], 'orphanRemoval' => true, ]); } } /** * Add mapping data to a translation entity. */ private function mapTranslation(ClassMetadata $metadata): void { $className = $metadata->name; try { $resourceMetadata = $this->resourceMetadataRegistry->getByClass($className); } catch (\InvalidArgumentException $exception) { return; } /** @var MetadataInterface $translatableResourceMetadata */ $translatableResourceMetadata = $this->resourceMetadataRegistry->get(str_replace('_translation', '', $resourceMetadata->getAlias())); if (!$metadata->hasAssociation('translatable')) { $metadata->mapManyToOne([ 'fieldName' => 'translatable', 'targetEntity' => $translatableResourceMetadata->getClass('model'), 'inversedBy' => 'translations', 'joinColumns' => [[ 'name' => 'translatable_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE', 'nullable' => false, ]], ]); } if (!$metadata->hasField('locale')) { $metadata->mapField([ 'fieldName' => 'locale', 'type' => 'string', 'nullable' => false, ]); } // Map unique index. $columns = [ $metadata->getSingleAssociationJoinColumnName('translatable'), 'locale', ]; if (!$this->hasUniqueConstraint($metadata, $columns)) { $constraints = $metadata->table['uniqueConstraints'] ?? []; $constraints[$metadata->getTableName() . '_uniq_trans'] = [ 'columns' => $columns, ]; $metadata->setPrimaryTable([ 'uniqueConstraints' => $constraints, ]); } } /** * Check if a unique constraint has been defined. */ private function hasUniqueConstraint(ClassMetadata $metadata, array $columns): bool { if (!isset($metadata->table['uniqueConstraints'])) { return false; } foreach ($metadata->table['uniqueConstraints'] as $constraint) { if (!array_diff($constraint['columns'], $columns)) { return true; } } return false; } private function processTranslatableEntityLocaleAssigner(object $translatableEntityLocaleAssigner): TranslatableEntityLocaleAssignerInterface { if ($translatableEntityLocaleAssigner instanceof ContainerInterface) { trigger_deprecation( 'sylius/resource-bundle', '1.4', 'Passing an instance of "%s" is deprecated. Use "%s" instead.', ContainerInterface::class, TranslatableEntityLocaleAssignerInterface::class, ); /** @var object $translatableEntityLocaleAssigner */ $translatableEntityLocaleAssigner = $translatableEntityLocaleAssigner->get('sylius.translatable_entity_locale_assigner'); } if (!$translatableEntityLocaleAssigner instanceof TranslatableEntityLocaleAssignerInterface) { throw new \InvalidArgumentException(sprintf( '`$translatableEntityLocaleAssigner` was expected to return an instance of "%s" , "%s" found', TranslatableEntityLocaleAssignerInterface::class, get_class($translatableEntityLocaleAssigner), )); } return $translatableEntityLocaleAssigner; } } ================================================ FILE: src/Bundle/ExpressionLanguage/ExpressionLanguage.php ================================================ delimiter = $delimiter; } public function transform($value): string { if (!($value instanceof Collection)) { throw new TransformationFailedException( sprintf( 'Expected "%s", but got "%s"', Collection::class, is_object($value) ? get_class($value) : gettype($value), ), ); } if ($value->isEmpty()) { return ''; } return implode($this->delimiter, $value->toArray()); } public function reverseTransform($value): Collection { if (!is_string($value)) { throw new TransformationFailedException( sprintf( 'Expected string, but got "%s"', is_object($value) ? get_class($value) : gettype($value), ), ); } if ('' === $value) { return new ArrayCollection(); } /** Explode would return string[]|false for PHP 7.4 and string[] for PHP 8 which messes in PHPStan algorithms */ return new ArrayCollection(explode($this->delimiter, $value) ?: []); // @phpstan-ignore-line } } ================================================ FILE: src/Bundle/Form/DataTransformer/RecursiveTransformer.php ================================================ decoratedTransformer = $decoratedTransformer; } /** @param Collection|null $value */ public function transform($value): ReadableCollection { if (null === $value) { return new ArrayCollection(); } $this->assertTransformationValueType($value, Collection::class); return $value->map( /** * @param mixed $currentValue * * @return mixed */ function ($currentValue) { return $this->decoratedTransformer->transform($currentValue); }, ); } /** @param Collection|null $value */ public function reverseTransform($value): ReadableCollection { if (null === $value) { return new ArrayCollection(); } $this->assertTransformationValueType($value, Collection::class); return $value->map( /** * @param mixed $currentValue * * @return mixed */ function ($currentValue) { return $this->decoratedTransformer->reverseTransform($currentValue); }, ); } /** * @param mixed $value * * @throws TransformationFailedException */ private function assertTransformationValueType($value, string $expectedType): void { if (!($value instanceof $expectedType)) { throw new TransformationFailedException( sprintf( 'Expected "%s", but got "%s"', $expectedType, is_object($value) ? get_class($value) : gettype($value), ), ); } } } ================================================ FILE: src/Bundle/Form/DataTransformer/ResourceToIdentifierTransformer.php ================================================ repository = $repository; $this->identifier = $identifier ?? 'id'; } /** * @inheritDoc */ public function transform(mixed $value): mixed { if (null === $value) { return null; } /** @psalm-suppress ArgumentTypeCoercion */ Assert::isInstanceOf($value, $this->repository->getClassName()); return PropertyAccess::createPropertyAccessor()->getValue($value, $this->identifier); } /** * @inheritDoc */ public function reverseTransform(mixed $value): ?ResourceInterface { if (null === $value) { return null; } /** @var ResourceInterface|null $resource */ $resource = $this->repository->findOneBy([$this->identifier => $value]); if (null === $resource) { throw new TransformationFailedException(sprintf( 'Object "%s" with identifier "%s"="%s" does not exist.', $this->repository->getClassName(), $this->identifier, $value, )); } return $resource; } } ================================================ FILE: src/Bundle/Form/EventSubscriber/AddCodeFormSubscriber.php ================================================ type = $type ?? TextType::class; $this->options = $options; } public static function getSubscribedEvents(): array { return [ FormEvents::PRE_SET_DATA => 'preSetData', ]; } public function preSetData(FormEvent $event): void { $resource = $event->getData(); $disabled = false; if ($resource instanceof CodeAwareInterface) { $disabled = null !== $resource->getCode(); } elseif (null !== $resource) { throw new UnexpectedTypeException($resource, CodeAwareInterface::class); } $form = $event->getForm(); $form->add('code', $this->type, array_merge( ['label' => 'sylius.ui.code'], $this->options, ['disabled' => $disabled], )); } } ================================================ FILE: src/Bundle/Form/Extension/CollectionTypeExtension.php ================================================ vars['button_add_label'] = $options['button_add_label']; $view->vars['button_delete_label'] = $options['button_delete_label']; } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'button_add_label' => 'sylius.form.collection.add', 'button_delete_label' => 'sylius.form.collection.delete', ]); } public function getExtendedType(): string { return CollectionType::class; } public static function getExtendedTypes(): iterable { return [CollectionType::class]; } } ================================================ FILE: src/Bundle/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php ================================================ serverParams = $serverParams ?: new ServerParams(); } public function handleRequest(FormInterface $form, mixed $request = null): void { if (!$request instanceof Request) { throw new UnexpectedTypeException($request, Request::class); } $name = $form->getName(); $method = $request->getMethod(); // For request methods that must not have a request body we fetch data // from the query string. Otherwise we look for data in the request body. if ('GET' === $method || 'HEAD' === $method || 'TRACE' === $method) { if ('' === $name) { /** @var array $data */ $data = $request->query->all(); } else { // Don't submit GET requests if the form's name does not exist // in the request if (!$request->query->has($name)) { return; } /** @var array $data */ $data = $request->query->all()[$name]; } } else { // Mark the form with an error if the uploaded size was too large // This is done here and not in FormValidator because $_POST is // empty when that error occurs. Hence the form is never submitted. if ($this->serverParams->hasPostMaxSizeBeenExceeded()) { // Submit the form, but don't clear the default values $form->submit(null, false); $uploadMaxSizeMessageCallable = $form->getConfig()->getOption('upload_max_size_message'); Assert::isCallable($uploadMaxSizeMessageCallable); $uploadMaxSizeMessage = call_user_func($uploadMaxSizeMessageCallable); Assert::string($uploadMaxSizeMessage); $form->addError(new FormError( $uploadMaxSizeMessage, null, ['{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()], )); return; } if ('' === $name) { $params = $request->request->all(); $files = $request->files->all(); } elseif ($request->request->has($name) || $request->files->has($name)) { /** @psalm-var array|null $default */ $default = $form->getConfig()->getCompound() ? [] : null; $params = $request->request->all()[$name] ?? $default; $files = $request->files->get($name, $default); } else { // Don't submit the form if it is not present in the request return; } if (is_array($params) && is_array($files)) { $data = array_replace_recursive($params, $files); } else { /** @var array $data */ $data = $params ?: $files; } } $form->submit($data, 'PATCH' !== $method); } public function isFileUpload(mixed $data): bool { return $data instanceof File; } } ================================================ FILE: src/Bundle/Form/Registry/FormTypeRegistry.php ================================================ formTypes[$identifier][$typeIdentifier] = $formType; } public function get(string $identifier, string $typeIdentifier): ?string { if (!$this->has($identifier, $typeIdentifier)) { return null; } return $this->formTypes[$identifier][$typeIdentifier]; } public function has(string $identifier, string $typeIdentifier): bool { return isset($this->formTypes[$identifier][$typeIdentifier]); } } ================================================ FILE: src/Bundle/Form/Registry/FormTypeRegistryInterface.php ================================================ dataClass = $dataClass; $this->validationGroups = $validationGroups; } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'data_class' => $this->dataClass, 'validation_groups' => $this->validationGroups, ]); } } ================================================ FILE: src/Bundle/Form/Type/ArchivableType.php ================================================ add('archivedAt', DateTimeType::class) ->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) { /** @var ArchivableInterface $archivable */ $archivable = $event->getData(); $archivedAt = null; if (null === $archivable->getArchivedAt()) { $archivedAt = new \DateTime(); } $archivable->setArchivedAt($archivedAt); $event->setData($archivable); }) ; } public function getBlockPrefix(): string { return 'sylius_archivable'; } } ================================================ FILE: src/Bundle/Form/Type/DefaultResourceType.php ================================================ metadataRegistry = $metadataRegistry; $this->formBuilderRegistry = $formBuilderRegistry; } public function buildForm(FormBuilderInterface $builder, array $options): void { Assert::string($options['data_class']); $metadata = $this->metadataRegistry->getByClass($options['data_class']); $driver = $metadata->getDriver(); Assert::notFalse($driver, sprintf( 'Form "%s" cannot be used with no driver configured on the resource "%s". Please define a form.', __CLASS__, $metadata->getAlias(), )); /** @var DefaultFormBuilderInterface $formBuilder */ $formBuilder = $this->formBuilderRegistry->get($driver); $formBuilder->build($metadata, $builder, $options); } public function getBlockPrefix(): string { return 'sylius_resource'; } } ================================================ FILE: src/Bundle/Form/Type/FixedCollectionType.php ================================================ add($entryName, $entryType, array_replace([ 'property_path' => '[' . $entryName . ']', 'block_name' => 'entry', ], $entryOptions)); } } public function configureOptions(OptionsResolver $resolver): void { $resolver->setRequired('entries'); $resolver->setAllowedTypes('entries', ['array', \Traversable::class]); $resolver->setRequired('entry_type'); $resolver->setAllowedTypes('entry_type', ['string', 'callable']); $resolver->setNormalizer('entry_type', $this->optionalCallableNormalizer()); $resolver->setRequired('entry_name'); $resolver->setAllowedTypes('entry_name', ['callable']); $resolver->setDefault('entry_options', function () { return []; }); $resolver->setAllowedTypes('entry_options', ['array', 'callable']); $resolver->setNormalizer('entry_options', $this->optionalCallableNormalizer()); } public function getBlockPrefix(): string { return 'sylius_fixed_collection'; } private function optionalCallableNormalizer(): \Closure { return /** * @param mixed $value * * @return mixed */ function (Options $options, $value) { if (is_callable($value)) { return $value; } return /** @return mixed */ function () use ($value) { return $value; }; } ; } } ================================================ FILE: src/Bundle/Form/Type/ResourceAutocompleteChoiceType.php ================================================ resourceRepositoryRegistry = $resourceRepositoryRegistry; } public function buildForm(FormBuilderInterface $builder, array $options): void { Assert::isInstanceOf($options['repository'], RepositoryInterface::class); Assert::nullOrString($options['choice_value']); if (!$options['multiple']) { $builder->addModelTransformer( new ResourceToIdentifierTransformer( $options['repository'], $options['choice_value'], ), ); } if ($options['multiple']) { $builder ->addModelTransformer( new RecursiveTransformer( new ResourceToIdentifierTransformer( $options['repository'], $options['choice_value'], ), ), ) ->addViewTransformer(new CollectionToStringTransformer(',')) ; } } /** * @psalm-suppress MissingPropertyType */ public function buildView(FormView $view, FormInterface $form, array $options): void { $view->vars['multiple'] = $options['multiple']; $view->vars['choice_name'] = $options['choice_name']; $view->vars['choice_value'] = $options['choice_value']; $view->vars['placeholder'] = $options['placeholder']; } public function configureOptions(OptionsResolver $resolver): void { $resolver ->setRequired([ 'resource', 'choice_name', 'choice_value', ]) ->setDefaults([ 'multiple' => false, 'error_bubbling' => false, 'placeholder' => '', 'repository' => function (Options $options) { Assert::string($options['resource']); return $this->resourceRepositoryRegistry->get($options['resource']); }, ]) ->setAllowedTypes('resource', ['string']) ->setAllowedTypes('multiple', ['bool']) ->setAllowedTypes('choice_name', ['string']) ->setAllowedTypes('choice_value', ['string']) ->setAllowedTypes('placeholder', ['string']) ; } public function getParent(): string { return HiddenType::class; } public function getBlockPrefix(): string { return 'sylius_resource_autocomplete_choice'; } } ================================================ FILE: src/Bundle/Form/Type/ResourceToIdentifierType.php ================================================ repository = $repository; $this->metadata = $metadata; } public function buildForm(FormBuilderInterface $builder, array $options): void { $identifier = $options['identifier']; Assert::nullOrString($identifier); $builder->addModelTransformer( new ResourceToIdentifierTransformer($this->repository, $identifier), ); } public function configureOptions(OptionsResolver $resolver): void { $resolver ->setDefaults([ 'identifier' => 'id', ]) ->setAllowedTypes('identifier', 'string') ; } public function getParent(): string { return EntityType::class; } public function getBlockPrefix(): string { return sprintf('%s_%s_to_identifier', $this->metadata->getApplicationName(), $this->metadata->getName()); } } ================================================ FILE: src/Bundle/Form/Type/ResourceTranslationsType.php ================================================ definedLocalesCodes = $localeProvider->getDefinedLocalesCodes(); $this->defaultLocaleCode = $localeProvider->getDefaultLocaleCode(); } public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) { /** @var TranslationInterface[]|null[] $translations */ $translations = $event->getData(); $parentForm = $event->getForm()->getParent(); Assert::notNull($parentForm); /** @var TranslatableInterface $translatable */ $translatable = $parentForm->getData(); foreach ($translations as $localeCode => $translation) { if (null === $translation) { unset($translations[$localeCode]); continue; } $translation->setLocale($localeCode); $translation->setTranslatable($translatable); } $event->setData($translations); }); } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'entries' => $this->definedLocalesCodes, 'entry_name' => function (string $localeCode): string { return $localeCode; }, 'entry_options' => function (string $localeCode): array { return [ 'required' => $localeCode === $this->defaultLocaleCode, ]; }, ]); } public function getParent(): string { return FixedCollectionType::class; } public function getBlockPrefix(): string { return 'sylius_translations'; } } ================================================ FILE: src/Bundle/Grid/Controller/ResourcesResolver.php ================================================ decoratedResolver = $decoratedResolver; $this->gridProvider = $gridProvider; $this->gridViewFactory = $gridViewFactory; } /** * @psalm-suppress MissingReturnType */ public function getResources(RequestConfiguration $requestConfiguration, RepositoryInterface $repository) { if (!$requestConfiguration->hasGrid()) { return $this->decoratedResolver->getResources($requestConfiguration, $repository); } $gridDefinition = $this->gridProvider->get($requestConfiguration->getGrid()); $request = $requestConfiguration->getRequest(); $parameters = new Parameters($request->query->all()); $gridView = $this->gridViewFactory->create($gridDefinition, $parameters, $requestConfiguration->getMetadata(), $requestConfiguration); if ($requestConfiguration->isHtmlRequest()) { return $gridView; } return $gridView->getData(); } } ================================================ FILE: src/Bundle/Grid/Parser/OptionsParser.php ================================================ container = $container; $this->expression = $expression; $this->propertyAccessor = $propertyAccessor; } /** * @param array|object|null $data */ public function parseOptions(array $parameters, Request $request, $data = null): array { return array_map( /** * @param mixed $parameter * * @return mixed */ function ($parameter) use ($request, $data) { if (is_array($parameter)) { return $this->parseOptions($parameter, $request, $data); } return $this->parseOption($parameter, $request, $data); }, $parameters, ); } /** * @param mixed $parameter * @param array|object|null $data * * @return mixed */ private function parseOption($parameter, Request $request, $data) { if (!is_string($parameter)) { return $parameter; } if (0 === strpos($parameter, '$')) { return $this->getFromRequest($request, substr($parameter, 1)); } if (0 === strpos($parameter, 'expr:')) { return $this->parseOptionExpression(substr($parameter, 5), $request); } if (0 === strpos($parameter, 'resource.')) { return $this->parseOptionResourceField(substr($parameter, 9), $data); } if (0 === strpos($parameter, 'resource[')) { return $this->parseOptionResourceField(substr($parameter, 8), $data); } return $parameter; } /** * @return mixed */ private function parseOptionExpression(string $expression, Request $request) { $expression = (string) preg_replace_callback( '/\$(\w+)/', /** @return callable */ function (array $matches) use ($request) { $variable = $this->getFromRequest($request, $matches[1]); return is_string($variable) ? sprintf('"%s"', addslashes($variable)) : $variable; }, $expression, ); return $this->expression->evaluate($expression, ['container' => $this->container]); } /** * @param array|object|null $data * * @return mixed */ private function parseOptionResourceField(string $value, $data) { return $this->propertyAccessor->getValue($data, $value); } } ================================================ FILE: src/Bundle/Grid/Parser/OptionsParserInterface.php ================================================ twig = $twig; $this->optionsParser = $optionsParser; $this->bulkActionTemplates = $bulkActionTemplates; } public function renderBulkAction(GridViewInterface $gridView, Action $bulkAction, $data = null): string { Assert::isInstanceOf($gridView, ResourceGridView::class); $type = $bulkAction->getType(); if (!isset($this->bulkActionTemplates[$type])) { throw new \InvalidArgumentException(sprintf('Missing template for bulk action type "%s".', $type)); } $options = $this->optionsParser->parseOptions( $bulkAction->getOptions(), $gridView->getRequestConfiguration()->getRequest(), $data, ); return $this->twig->render($this->bulkActionTemplates[$type], [ 'grid' => $gridView, 'action' => $bulkAction, 'data' => $data, 'options' => $options, ]); } } ================================================ FILE: src/Bundle/Grid/Renderer/TwigGridRenderer.php ================================================ gridRenderer = $gridRenderer; $this->twig = $twig; $this->optionsParser = $optionsParser; $this->actionTemplates = $actionTemplates; } public function render(GridViewInterface $gridView, ?string $template = null): string { return $this->gridRenderer->render($gridView, $template); } /** * @param mixed $data */ public function renderField(GridViewInterface $gridView, Field $field, $data): string { return $this->gridRenderer->renderField($gridView, $field, $data); } /** * @param mixed $data */ public function renderAction(GridViewInterface $gridView, Action $action, $data = null): string { if (!$gridView instanceof ResourceGridView) { return $this->gridRenderer->renderAction($gridView, $action, $data); } $type = $action->getType(); $template = method_exists($action, 'getTemplate') ? $action->getTemplate() : null; $template ??= $this->actionTemplates[$type] ?? null; if (null === $template) { throw new \InvalidArgumentException(sprintf('Missing template for action type "%s".', $type)); } $options = $this->optionsParser->parseOptions( $action->getOptions(), $gridView->getRequestConfiguration()->getRequest(), $data, ); return $this->twig->render($template, [ 'grid' => $gridView, 'action' => $action, 'data' => $data, 'options' => $options, ]); } public function renderFilter(GridViewInterface $gridView, Filter $filter): string { return $this->gridRenderer->renderFilter($gridView, $filter); } } ================================================ FILE: src/Bundle/Grid/View/LegacyGridViewFactory.php ================================================ get(RequestConfigurationOption::class)?->requestConfiguration(); $metadata = $context->get(MetadataOption::class)?->metadata(); if (null === $requestConfiguration || null === $metadata) { return $this->decorated->create($grid, $context, $parameters, $driverConfiguration); } return $this->resourceGridViewFactory->create($grid, $parameters, $metadata, $requestConfiguration); } } ================================================ FILE: src/Bundle/Grid/View/ResourceGridView.php ================================================ metadata = $resourceMetadata; $this->requestConfiguration = $requestConfiguration; } public function getMetadata(): MetadataInterface { return $this->metadata; } public function getRequestConfiguration(): RequestConfiguration { return $this->requestConfiguration; } } ================================================ FILE: src/Bundle/Grid/View/ResourceGridViewFactory.php ================================================ dataProvider = $dataProvider; $this->parametersParser = $parametersParser; } public function create( Grid $grid, Parameters $parameters, MetadataInterface $metadata, RequestConfiguration $requestConfiguration, ): ResourceGridView { $driverConfiguration = $grid->getDriverConfiguration(); $request = $requestConfiguration->getRequest(); $grid->setDriverConfiguration($this->parametersParser->parseRequestValues($driverConfiguration, $request)); return new ResourceGridView($this->dataProvider->getData($grid, $parameters), $grid, $parameters, $metadata, $requestConfiguration); } } ================================================ FILE: src/Bundle/Grid/View/ResourceGridViewFactoryInterface.php ================================================ attributes->get($key, $request)) { return $result; } if ($request->query->has($key)) { return $request->query->all()[$key]; } if ($request->request->has($key)) { return $request->request->all()[$key]; } return $default; } } ================================================ FILE: src/Bundle/ResourceBundleInterface.php ================================================ ================================================ FILE: src/Bundle/Resources/config/services/console.php ================================================ services(); $services->defaults() ->public(); $services->set('sylius.console.command.resource_debug', DebugResourceCommand::class) ->args([ service('sylius.resource_registry'), service('sylius.resource_metadata_collection.factory'), ]) ->tag('console.command'); $services->alias(DebugResourceCommand::class, 'sylius.console.command.resource_debug'); }; ================================================ FILE: src/Bundle/Resources/config/services/context.php ================================================ services(); $services->set('sylius.context.initiator.request_context', RequestContextInitiator::class); $services->alias(RequestContextInitiatorInterface::class, 'sylius.context.initiator.request_context'); $services->set('sylius.context.initiator.legacy_request_context', LegacyRequestContextInitiator::class) ->decorate('sylius.context.initiator.request_context') ->args([ service('sylius.resource_registry'), service('sylius.resource_controller.request_configuration_factory'), service('.inner'), service('sylius.expression_language.vars_resolver.metadata'), ]); }; ================================================ FILE: src/Bundle/Resources/config/services/controller.php ================================================ services(); $services->set('sylius.main_controller', MainController::class) ->args([ service('sylius.resource_metadata_operation.initiator.http_operation'), service('sylius.context.initiator.request_context'), service('sylius.state_provider.main'), service('sylius.state_processor.main'), ]) ->tag('controller.service_arguments'); $services->set('sylius.resource_controller.parameters_parser', ParametersParser::class) ->args([ service('service_container'), service('sylius.expression_language'), ]); $services->alias(ParametersParserInterface::class, 'sylius.resource_controller.parameters_parser'); $services->set('sylius.resource_controller.request_configuration_factory', RequestConfigurationFactory::class) ->args([ service('sylius.resource_controller.parameters_parser'), RequestConfiguration::class, '%sylius.resource.settings%', ]); $services->alias(RequestConfigurationFactoryInterface::class, 'sylius.resource_controller.request_configuration_factory'); $services->set('sylius.resource_controller.new_resource_factory', NewResourceFactory::class); $services->alias(NewResourceFactoryInterface::class, 'sylius.resource_controller.new_resource_factory'); $services->set('sylius.resource_controller.single_resource_provider', SingleResourceProvider::class); $services->alias(SingleResourceProviderInterface::class, 'sylius.resource_controller.single_resource_provider'); $services->set('sylius.resource_controller.pagerfanta_representation_factory', PagerfantaFactory::class); $services->alias(PagerfantaFactory::class, 'sylius.resource_controller.pagerfanta_representation_factory'); $services->set('sylius.resource_controller.resources_resolver', ResourcesResolver::class); $services->alias(ResourcesResolverInterface::class, 'sylius.resource_controller.resources_resolver'); $services->set('sylius.resource_controller.resources_collection_provider', ResourcesCollectionProvider::class) ->args([ service('sylius.resource_controller.resources_resolver'), service('sylius.resource_controller.pagerfanta_representation_factory')->nullOnInvalid(), ]); $services->alias(ResourcesCollectionProviderInterface::class, 'sylius.resource_controller.resources_collection_provider'); $services->set('sylius.resource_controller.form_factory', ResourceFormFactory::class) ->args([service('form.factory')]); $services->alias(ResourceFormFactoryInterface::class, 'sylius.resource_controller.form_factory'); $services->set('sylius.resource_controller.redirect_handler', RedirectHandler::class) ->args([service('router')]); $services->alias(RedirectHandlerInterface::class, 'sylius.resource_controller.redirect_handler'); $services->set('sylius.resource_controller.authorization_checker.disabled', DisabledAuthorizationChecker::class); $services->alias(DisabledAuthorizationChecker::class, 'sylius.resource_controller.authorization_checker.disabled'); $services->set('sylius.resource_controller.flash_helper', FlashHelper::class) ->args([ service('request_stack'), service('translator'), '%locale%', ]); $services->alias(FlashHelperInterface::class, 'sylius.resource_controller.flash_helper'); $services->set('sylius.resource_controller.event_dispatcher', EventDispatcher::class) ->args([service('event_dispatcher')]); $services->alias(EventDispatcherInterface::class, 'sylius.resource_controller.event_dispatcher'); $services->set('sylius.resource_controller.view_handler', ViewHandler::class) ->args([service('fos_rest.view_handler')->nullOnInvalid()]); $services->alias(ViewHandlerInterface::class, 'sylius.resource_controller.view_handler'); $services->set('sylius.resource_controller.resource_update_handler', ResourceUpdateHandler::class) ->args([service('sylius.resource_controller.state_machine')->nullOnInvalid()]); $services->alias(ResourceUpdateHandlerInterface::class, 'sylius.resource_controller.resource_update_handler'); $services->set('sylius.resource_controller.resource_delete_handler', ResourceDeleteHandler::class); $services->alias(ResourceDeleteHandlerInterface::class, 'sylius.resource_controller.resource_delete_handler'); }; ================================================ FILE: src/Bundle/Resources/config/services/dispatcher.php ================================================ services(); $services->set('sylius.dispatcher.operation', OperationEventDispatcher::class) ->args([service('event_dispatcher')]); $services->alias(OperationEventDispatcherInterface::class, 'sylius.dispatcher.operation'); $services->set('sylius.event_handler.operation', OperationEventHandler::class) ->args([ service('sylius.routing.redirect_handler'), service('sylius.helper.flash'), ]); $services->alias(OperationEventHandlerInterface::class, 'sylius.event_handler.operation'); }; ================================================ FILE: src/Bundle/Resources/config/services/expression_language.php ================================================ services(); $services->set('sylius.metadata.expression_language', ExpressionLanguage::class); $services->set('sylius.resource_factory.expression_language', ExpressionLanguage::class); $services->set('sylius.repository.expression_language', ExpressionLanguage::class); $services->set('sylius.routing.expression_language', ExpressionLanguage::class); $services->set('sylius.expression_language.variables.token', TokenVariables::class) ->args([service('security.token_storage')->nullOnInvalid()]) ->tag('sylius.metadata_variables') ->tag('sylius.resource_factory_variables') ->tag('sylius.repository_variables'); $services->set('sylius.expression_language.variables.request', RequestVariables::class) ->args([service('request_stack')]) ->tag('sylius.metadata_variables') ->tag('sylius.resource_factory_variables') ->tag('sylius.repository_variables') ->tag('sylius.routing_variables'); $services->set('sylius.expression_language.variables.sylius_repositories', SyliusRepositoriesVariables::class) ->args([tagged_locator('sylius.repository')]) ->tag('sylius.metadata_variables') ->tag('sylius.resource_factory_variables'); $services->set('sylius.expression_language.variables_collection.metadata', VariablesCollection::class) ->args([tagged_iterator('sylius.metadata_variables')]); $services->set('sylius.expression_language.variables_collection.factory', VariablesCollection::class) ->args([tagged_iterator('sylius.resource_factory_variables')]); $services->set('sylius.expression_language.variables_collection.form', VariablesCollection::class) ->args([tagged_iterator('sylius.form_variables')]); $services->set('sylius.expression_language.variables_collection.repository', VariablesCollection::class) ->args([tagged_iterator('sylius.repository_variables')]); $services->set('sylius.expression_language.variables_collection.routing', VariablesCollection::class) ->args([tagged_iterator('sylius.routing_variables')]); $services->set('sylius.expression_language.providers.throw_not_found_on_null', ThrowNotFoundOnNullExpressionFunctionProvider::class) ->tag('sylius.metadata_providers') ->tag('sylius.resource_factory_providers'); $services->set('sylius.expression_language.vars_resolver.metadata', VarsResolver::class) ->args([service('sylius.expression_language.argument_parser.metadata')]); $services->set('sylius.expression_language.argument_parser.metadata', ArgumentParser::class) ->args([ service('sylius.metadata.expression_language'), service('sylius.expression_language.variables_collection.metadata'), tagged_iterator('sylius.metadata_providers'), ]); $services->set('sylius.expression_language.argument_parser.factory', ArgumentParser::class) ->args([ service('sylius.resource_factory.expression_language'), service('sylius.expression_language.variables_collection.factory'), tagged_iterator('sylius.resource_factory_providers'), ]); $services->set('sylius.expression_language.argument_parser.form', ArgumentParser::class) ->args([ service('sylius.resource_factory.expression_language'), service('sylius.expression_language.variables_collection.form'), tagged_iterator('sylius.resource_factory_providers'), ]); $services->set('sylius.expression_language.argument_parser.repository', ArgumentParser::class) ->args([ service('sylius.repository.expression_language'), service('sylius.expression_language.variables_collection.repository'), tagged_iterator('sylius.repository_providers'), ]); $services->set('sylius.expression_language.argument_parser.routing', ArgumentParser::class) ->args([ service('sylius.routing.expression_language'), service('sylius.expression_language.variables_collection.routing'), tagged_iterator('sylius.routing_providers'), ]); }; ================================================ FILE: src/Bundle/Resources/config/services/form.php ================================================ services(); $services->defaults() ->public(); $services->set('sylius.form.type.resource_autocomplete_choice', ResourceAutocompleteChoiceType::class) ->args([service('sylius.registry.resource_repository')]) ->tag('form.type'); $services->alias(ResourceAutocompleteChoiceTypeInterface::class, 'sylius.form.type.resource_autocomplete_choice'); $services->set('sylius.form.factory', FormFactory::class) ->private() ->args([ service('form.factory'), service('sylius.expression_language.argument_parser.form'), ]); $services->alias(FormFactoryInterface::class, 'sylius.form.factory'); }; ================================================ FILE: src/Bundle/Resources/config/services/helper.php ================================================ services(); $services->set('sylius.helper.flash', FlashHelper::class) ->args([service('translator')]); $services->alias(FlashHelperInterface::class, 'sylius.helper.flash'); }; ================================================ FILE: src/Bundle/Resources/config/services/integrations/doctrine/mongodb-odm.php ================================================ services(); $parameters = $container->parameters(); $parameters->set('sylius.mongodb_odm.repository.class', DocumentRepository::class); $services->defaults() ->public(); $services->set('sylius.event_subscriber.odm_mapped_super_class', ODMMappedSuperClassSubscriber::class) ->args([service('sylius.resource_registry')]) ->tag('doctrine_mongodb.odm.event_subscriber', ['priority' => 8192]) ->deprecate('sylius/resource-bundle', '1.3', 'The "%service_id%" service is deprecated since sylius/resource-bundle 1.3. Doctrine MongoDB and PHPCR support will no longer be supported in 2.0.'); $services->set('sylius.event_subscriber.odm_repository_class', ODMRepositoryClassSubscriber::class) ->args([service('sylius.resource_registry')]) ->tag('doctrine_mongodb.odm.event_subscriber', ['priority' => 8192]) ->deprecate('sylius/resource-bundle', '1.3', 'The "%service_id%" service is deprecated since sylius/resource-bundle 1.3. Doctrine MongoDB and PHPCR support will no longer be supported in 2.0.'); }; ================================================ FILE: src/Bundle/Resources/config/services/integrations/doctrine/orm.php ================================================ services(); $parameters = $container->parameters(); $parameters->set('sylius.orm.repository.class', EntityRepository::class); $parameters->set('sylius.translation.translatable_listener.doctrine.orm.class', TranslatableListener::class); $services->defaults() ->public(); $services->set('sylius.event_subscriber.orm_mapped_super_class', ORMMappedSuperClassSubscriber::class) ->args([service('sylius.resource_registry')]) ->tag('doctrine.event_listener', ['event' => 'loadClassMetadata', 'priority' => 8192]); $services->alias(ORMMappedSuperClassSubscriber::class, 'sylius.event_subscriber.orm_mapped_super_class'); $services->set('sylius.event_subscriber.orm_repository_class', ORMRepositoryClassSubscriber::class) ->args([service('sylius.resource_registry')]) ->tag('doctrine.event_listener', ['event' => 'loadClassMetadata', 'priority' => 8192]); $services->alias(ORMRepositoryClassSubscriber::class, 'sylius.event_subscriber.orm_repository_class'); $services->set('sylius.form_builder.default', DefaultFormBuilder::class) ->private() ->args([service('doctrine.orm.default_entity_manager')]) ->tag('sylius.default_resource_form.builder', ['type' => 'doctrine/orm']); $services->alias(DefaultFormBuilderInterface::class, 'sylius.form_builder.default') ->private(); $services->set('sylius.doctrine.orm.container_repository_factory', ContainerRepositoryFactory::class) ->private() ->decorate('doctrine.orm.container_repository_factory') ->args([ service('sylius.doctrine.orm.container_repository_factory.inner'), '%sylius.doctrine.orm.container_repository_factory.entities%', ]); $services->alias(ContainerRepositoryFactory::class, 'sylius.doctrine.orm.container_repository_factory') ->private(); }; ================================================ FILE: src/Bundle/Resources/config/services/integrations/doctrine/phpcr-odm.php ================================================ parameters(); $parameters->set('sylius.phpcr_odm.repository.class', DocumentRepository::class); }; ================================================ FILE: src/Bundle/Resources/config/services/integrations/doctrine.php ================================================ services(); $services->set(ResourceMappingDriverChain::class) ->public() ->decorate('doctrine.orm.default_metadata_driver') ->args([ service(ResourceMappingDriverChain::class . '.inner'), service('sylius.resource_registry'), ]); $services->alias('sylius_resource.doctrine.mapping_driver_chain', ResourceMappingDriverChain::class); $services->set(PersistProcessor::class) ->args([service('doctrine')]) ->tag('sylius.state_processor'); $services->set(RemoveProcessor::class) ->args([service('doctrine')]) ->tag('sylius.state_processor'); }; ================================================ FILE: src/Bundle/Resources/config/services/integrations/grid.php ================================================ services(); $services->defaults() ->public(); $services->set('sylius.grid.resource_view_factory', ResourceGridViewFactory::class) ->args([ service('sylius.grid.data_provider'), service('sylius.resource_controller.parameters_parser'), ]); $services->alias(ResourceGridViewFactoryInterface::class, 'sylius.grid.resource_view_factory'); $services->set('sylius.resource_controller.resources_resolver.grid_aware', ResourcesResolver::class) ->decorate('sylius.resource_controller.resources_resolver', null, 256) ->args([ service('sylius.resource_controller.resources_resolver.grid_aware.inner'), service('sylius.grid.provider'), service('sylius.grid.resource_view_factory'), ]); $services->alias(ResourcesResolverInterface::class, 'sylius.resource_controller.resources_resolver.grid_aware'); $services->set('sylius.custom_grid_renderer.twig', TwigGridRenderer::class) ->decorate('sylius.grid.renderer.twig', null, 256) ->args([ service('sylius.custom_grid_renderer.twig.inner'), service('twig'), service('sylius.grid_options_parser'), '%sylius.grid.templates.action%', ]); $services->alias(TwigGridRendererInterface::class, 'sylius.custom_grid_renderer.twig'); $services->set('sylius.custom_bulk_action_grid_renderer.twig', TwigBulkActionGridRenderer::class) ->decorate('sylius.grid.bulk_action_renderer.twig', null, 256) ->args([ service('twig'), service('sylius.grid_options_parser'), '%sylius.grid.templates.bulk_action%', ]); $services->alias(TwigBulkActionGridRendererInterface::class, 'sylius.custom_bulk_action_grid_renderer.twig'); $services->set('sylius.grid_options_parser', OptionsParser::class) ->private() ->args([ service('service_container'), service('sylius.expression_language'), service('property_accessor'), ]); $services->alias(OptionsParserInterface::class, 'sylius.grid_options_parser') ->private(); $services->set('sylius.grid.view_factory.legacy', LegacyGridViewFactory::class) ->private() ->decorate('sylius.grid.view_factory.resource') ->args([ service('sylius.grid.resource_view_factory'), service('.inner'), ]); $services->set('sylius.grid.view_factory.resource', GridViewFactory::class) ->args([service('sylius.grid.data_provider')]); $services->alias(GridViewFactoryInterface::class, 'sylius.grid.view_factory.resource'); }; ================================================ FILE: src/Bundle/Resources/config/services/integrations/translation.php ================================================ services(); $services->defaults() ->public(); $services->set('sylius.translation_locale_provider.immutable', ResourceImmutableTranslationLocaleProvider::class) ->args([ ['' => '%locale%'], '%locale%', ]); $services->alias(ComponentImmutableTranslationLocaleProvider::class, 'sylius.translation_locale_provider.immutable') ->deprecate('sylius/resource-bundle', '1.11', 'The "%alias_id%" service alias is deprecated since sylius/resource-bundle 1.11 and will be removed in sylius/resource-bundle 2.0. Use Sylius\Resource\Translation\Provider\ImmutableTranslationLocaleProvider instead.'); $services->alias(ResourceImmutableTranslationLocaleProvider::class, 'sylius.translation_locale_provider.immutable'); $services->alias(TranslationLocaleProviderInterface::class, 'sylius.translation_locale_provider.immutable'); $services->set('sylius.translation.translatable_listener.doctrine.orm', ORMTranslatableListener::class) ->args([ service('sylius.resource_registry'), service('sylius.translatable_entity_locale_assigner'), ]) ->tag('doctrine.event_listener', ['connection' => 'default', 'event' => 'loadClassMetadata', 'priority' => 99]) ->tag('doctrine.event_listener', ['connection' => 'default', 'event' => 'postLoad', 'priority' => 99]); $services->alias(ORMTranslatableListener::class, 'sylius.translation.translatable_listener.doctrine.orm'); $services->set('sylius.form.type.resource_translations', ResourceTranslationsType::class) ->args([service('sylius.translation_locale_provider')]) ->tag('form.type'); $services->alias(ResourceTranslationsTypeInterface::class, 'sylius.form.type.resource_translations'); $services->set('sylius.translatable_entity_locale_assigner', TranslatableEntityLocaleAssigner::class) ->args([service('sylius.translation_locale_provider')]); $services->alias(TranslatableEntityLocaleAssignerInterface::class, 'sylius.translatable_entity_locale_assigner'); }; ================================================ FILE: src/Bundle/Resources/config/services/listener.php ================================================ services(); $services->set('sylius.negotiator', Negotiator::class); $services->set('sylius.listener.add_format', AddFormatListener::class) ->args([ service('sylius.resource_metadata_operation.initiator.http_operation'), service('sylius.negotiator'), ]) ->tag('kernel.event_listener', ['event' => 'kernel.request', 'priority' => 28]); $services->set('sylius.listener.exception.validation', ValidationExceptionListener::class) ->args([service('serializer')->nullOnInvalid()]) ->tag('kernel.event_listener', ['event' => 'kernel.exception', 'method' => 'onKernelException']) ->lazy(); }; ================================================ FILE: src/Bundle/Resources/config/services/metadata/extractor.php ================================================ services(); $services->set('sylius.metadata.resource_extractor.php_file', PhpFileResourceExtractor::class) ->args([ [], service('service_container'), ]); }; ================================================ FILE: src/Bundle/Resources/config/services/metadata/inflector.php ================================================ services(); $services->set('sylius.metadata.inflector', Inflector::class); }; ================================================ FILE: src/Bundle/Resources/config/services/metadata/mutator.php ================================================ services(); $services->set('sylius.metadata.mutator_collection.resource', ResourceMutatorCollection::class) ->private(); $services->set('sylius.metadata.mutator_collection.operation', OperationMutatorCollection::class) ->private(); }; ================================================ FILE: src/Bundle/Resources/config/services/metadata/path_segment_name_generator.php ================================================ services(); $services->set('sylius.metadata.path_segment_name_generator.underscore', UnderscorePathSegmentNameGenerator::class) ->args([ service('sylius.metadata.inflector'), ]) ; $services->set('sylius.metadata.path_segment_name_generator.dash', DashPathSegmentNameGenerator::class) ->args([ service('sylius.metadata.inflector'), ]) ; }; ================================================ FILE: src/Bundle/Resources/config/services/metadata/repository_argument_resolver.php ================================================ services(); $services->set('sylius.repository_argument_resolver.request', RepositoryArgumentResolver::class); }; ================================================ FILE: src/Bundle/Resources/config/services/metadata/resource_class_list.php ================================================ services(); $services->set('sylius.cache.metadata.resource_class_list') ->private() ->parent('cache.system') ->tag('cache.pool'); $services->alias('sylius.metadata.resource_class_list.factory', 'sylius.metadata.resource_class_list.factory.attributes'); $services->alias(ResourceClassListFactoryInterface::class, 'sylius.metadata.resource_class_list.factory'); $services->set('sylius.metadata.resource_class_list.factory.attributes', AttributesResourceClassListFactory::class) ->args(['%sylius.resource.mapping%']); $services->alias(AttributesResourceClassListFactoryInterface::class, 'sylius.metadata.resource_class_list.factory.attributes'); $services->set('sylius.metadata.resource_class_list.cached', CachedResourceClassListFactory::class) ->decorate('sylius.metadata.resource_class_list.factory', null, -10) ->args([ service('sylius.cache.metadata.resource_class_list'), service('.inner'), ]); $services->set('sylius.metadata.resource_class_list.factory.php_file', PhpFileResourceClassListFactory::class) ->decorate('sylius.metadata.resource_class_list.factory', null, 100) ->args([ service('sylius.metadata.resource_extractor.php_file'), service('.inner'), ]); $services->alias(PhpFileResourceClassListFactoryInterface::class, 'sylius.metadata.resource_class_list.factory.php_file'); }; ================================================ FILE: src/Bundle/Resources/config/services/metadata/resource_metadata_collection.php ================================================ services(); $services->set('sylius.cache.metadata.resource_collection') ->private() ->parent('cache.system') ->tag('cache.pool'); $services->alias('sylius.resource_metadata_collection.factory', 'sylius.resource_metadata_collection.factory.attributes'); $services->alias(ResourceMetadataCollectionFactoryInterface::class, 'sylius.resource_metadata_collection.factory.attributes'); $services->set('sylius.resource_metadata_collection.factory.attributes', AttributesResourceMetadataCollectionFactory::class) ->args([ service('sylius.resource_registry'), service('sylius.routing.factory.operation_route_name_factory'), ]); $services->set('sylius.resource_metadata_collection.factory.php_file', PhpFileResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory', null, 400) ->args([ service('sylius.resource_registry'), service('sylius.routing.factory.operation_route_name_factory'), service('sylius.metadata.resource_extractor.php_file'), service('.inner'), ]); $services->set('sylius.metadata.resource.metadata_collection_factory.mutator', MutatorResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory', null, 400) ->args([ service('sylius.metadata.mutator_collection.resource'), service('sylius.metadata.mutator_collection.operation'), service('.inner'), ]); $services->set('sylius.resource_metadata_collection.factory.plural_name', PluralNameResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory', null, 300) ->args([ service('.inner'), service('sylius.metadata.inflector'), param('sylius.routing_path_bc_layer'), service('sylius.resource_registry'), ]); $services->set('sylius.resource_metadata_collection.factory.state_machine', StateMachineResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory', null, 300) ->args([ service('sylius.resource_registry'), service('.inner'), '%sylius.state_machine_component.default%', ]); $services->set('sylius.resource_metadata_collection.factory.doctrine', DoctrineResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory', null, 200) ->args([ service('sylius.resource_registry'), service('.inner'), ]); $services->set('sylius.resource_metadata_collection.factory.redirect', RedirectResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory', null, 200) ->args([ service('sylius.routing.factory.operation_route_name_factory'), service('.inner'), ]); $services->set('sylius.resource_metadata_collection.factory.vars', VarsResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory') ->args([service('.inner')]); $services->set('sylius.resource_metadata_collection.factory.provider', ProviderResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory') ->args([service('.inner')]); $services->set('sylius.resource_metadata_collection.factory.resource_factory', FactoryResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory') ->args([ service('sylius.resource_registry'), service('.inner'), ]); $services->set('sylius.resource_metadata_collection.factory.event_short_name', EventShortNameResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory') ->args([service('.inner')]); $services->set('sylius.resource_metadata_collection.factory.templates_dir', TemplatesDirResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory') ->args([ service('.inner'), '%sylius.resource.settings%', ]); $services->set('sylius.resource_metadata_collection.factory.cached', CachedResourceMetadataCollectionFactory::class) ->decorate('sylius.resource_metadata_collection.factory', null, -10) ->args([ service('sylius.cache.metadata.resource_collection'), service('.inner'), ]); }; ================================================ FILE: src/Bundle/Resources/config/services/metadata/resource_metadata_operation.php ================================================ services(); $services->set('sylius.resource_metadata_operation.initiator.http_operation', HttpOperationInitiator::class) ->args([ service('sylius.resource_registry'), service('sylius.resource_metadata_collection.factory'), service('sylius.expression_language.vars_resolver.metadata'), ]); $services->alias(HttpOperationInitiatorInterface::class, 'sylius.resource_metadata_operation.initiator.http_operation'); }; ================================================ FILE: src/Bundle/Resources/config/services/metadata.php ================================================ import('metadata/**/**.php'); }; ================================================ FILE: src/Bundle/Resources/config/services/routing/loader.php ================================================ services(); $services->set('sylius.symfony.routing.loader.resource', ResourceLoader::class) ->args([ service('sylius.metadata.resource_class_list.factory'), service('sylius.routing.resource.route_collection_factory'), ]) ->tag('routing.route_loader'); $services->alias(ResourceLoaderInterface::class, 'sylius.symfony.routing.loader.resource'); }; ================================================ FILE: src/Bundle/Resources/config/services/routing/resource.php ================================================ services(); $services->set('sylius.routing.resource.route_collection_factory', ResourceRouteCollectionFactory::class) ->args([ service('sylius.routing.factory.operation_route'), service('sylius.resource_metadata_collection.factory'), service('sylius.resource_registry'), ]); $services->alias(ResourceRouteCollectionFactoryInterface::class, 'sylius.routing.resource.route_collection_factory'); }; ================================================ FILE: src/Bundle/Resources/config/services/routing.php ================================================ services(); $container->import('routing/**/**.php'); $services->defaults() ->public(); $services->set('sylius.routing.loader.resource', ResourceLoader::class) ->private() ->args([ service('sylius.resource_registry'), inline_service(RouteFactory::class), '%kernel.environment%', '%sylius.routing_path_bc_layer%', ]) ->tag('routing.loader') ->deprecate('sylius/resource', '1.13', 'The "%service_id%" service is deprecated since sylius/resource-bundle 1.13 and will be removed in sylius/resource-bundle 2.0. Use "sylius.symfony.routing.loader.resource" instead.'); $services->alias(ResourceLoader::class, 'sylius.routing.loader.resource') ->private(); $services->set('sylius.routing.loader.crud_routes_attributes', CrudRoutesAttributesLoader::class) ->private() ->args([ '%sylius.resource.mapping%', service('sylius.routing.loader.resource'), ]) ->tag('routing.route_loader'); $services->alias(CrudRoutesAttributesLoader::class, 'sylius.routing.loader.crud_routes_attributes') ->private(); $services->set('sylius.routing.loader.routes_attributes', RoutesAttributesLoader::class) ->private() ->args([ '%sylius.resource.mapping%', service('sylius.routing.factory.route_attributes'), service('sylius.routing.factory.attributes_operation_route'), ]) ->tag('routing.route_loader') ->deprecate('sylius/resource', '1.13', 'The "%service_id%" service is deprecated since sylius/resource-bundle 1.13 and will be removed in sylius/resource-bundle 2.0. Use "sylius.symfony.routing.loader.resource" instead.'); $services->alias(RoutesAttributesLoader::class, 'sylius.routing.loader.routes_attributes') ->private(); $services->set('sylius.routing.factory.operation_route_name_factory', OperationRouteNameFactory::class) ->private(); $services->alias(OperationRouteNameFactoryInterface::class, 'sylius.routing.factory.operation_route_name_factory'); $services->set('sylius.routing.factory.operation_route_path_factory.default', OperationRoutePathFactory::class) ->private(); $services->alias('sylius.routing.factory.operation_route_path_factory', 'sylius.routing.factory.operation_route_path_factory.default'); $services->alias(OperationRoutePathFactoryInterface::class, 'sylius.routing.factory.operation_route_path_factory'); $services->set('sylius.routing.factory.operation_route_path_factory.collection', CollectionOperationRoutePathFactory::class) ->decorate('sylius.routing.factory.operation_route_path_factory.default', null, 60) ->args([ service('.inner'), service('sylius.path_segment_name_generator'), ]) ; $services->set('sylius.routing.factory.operation_route_path_factory.create', CreateOperationRoutePathFactory::class) ->decorate('sylius.routing.factory.operation_route_path_factory.default', null, -50) ->args([ service('.inner'), service('sylius.path_segment_name_generator'), ]) ; $services->set('sylius.routing.factory.operation_route_path_factory.bulk_operation', BulkOperationRoutePathFactory::class) ->decorate('sylius.routing.factory.operation_route_path_factory.default', null, -40) ->args([ service('.inner'), service('sylius.path_segment_name_generator'), ]); $services->set('sylius.routing.factory.operation_route_path_factory.update', UpdateOperationRoutePathFactory::class) ->decorate('sylius.routing.factory.operation_route_path_factory.default', null, -30) ->args([ service('.inner'), service('sylius.path_segment_name_generator'), ]) ; $services->set('sylius.routing.factory.operation_route_path_factory.delete', DeleteOperationRoutePathFactory::class) ->decorate('sylius.routing.factory.operation_route_path_factory.default', null, -20) ->args([ service('.inner'), service('sylius.path_segment_name_generator'), ]) ; $services->set('sylius.routing.factory.operation_route_path_factory.show', ShowOperationRoutePathFactory::class) ->decorate('sylius.routing.factory.operation_route_path_factory.default', null, -10) ->args([ service('.inner'), service('sylius.path_segment_name_generator'), ]) ; $services->set('sylius.routing.factory.route_attributes', RouteAttributesFactory::class) ->private(); $services->alias(RouteAttributesFactoryInterface::class, 'sylius.routing.factory.route_attributes'); $services->set('sylius.routing.factory.attributes_operation_route', AttributesOperationRouteFactory::class) ->private() ->args([ service('sylius.resource_registry'), service('sylius.routing.factory.operation_route'), service('sylius.resource_metadata_collection.factory'), ]) ->deprecate('sylius/resource-bundle', '1.13', 'The "%service_id%" service is deprecated since sylius/resource-bundle 1.13 and will be removed in sylius/resource-bundle 2.0. Use "sylius.routing.resource.route_collection_factory" instead.'); $services->alias(AttributesOperationRouteFactoryInterface::class, 'sylius.routing.factory.attributes_operation_route') ->deprecate('sylius/resource-bundle', '1.13', 'The "%alias_id%" service is deprecated since sylius/resource-bundle 1.13 and will be removed in sylius/resource-bundle 2.0. Use "sylius.routing.resource.route_collection_factory" instead.'); $services->set('sylius.routing.factory.operation_route', OperationRouteFactory::class) ->private() ->args([ service('sylius.routing.factory.operation_route_path_factory'), service('sylius.path_segment_name_generator'), param('sylius.routing_path_bc_layer'), ]); $services->alias(OperationRouteFactoryInterface::class, 'sylius.routing.factory.operation_route'); $services->set('sylius.routing.redirect_handler', RedirectHandler::class) ->args([ service('router'), service('sylius.expression_language.argument_parser.routing'), service('sylius.routing.factory.operation_route_name_factory'), service('sylius.grid.filter_storage')->nullOnInvalid(), ]); $services->alias(RedirectHandlerInterface::class, 'sylius.routing.redirect_handler'); }; ================================================ FILE: src/Bundle/Resources/config/services/security.php ================================================ services(); $services->set('sylius.security.operation_access_checker', OperationAccessChecker::class) ->args([ service('sylius.expression_language')->nullOnInvalid(), service('security.authentication.trust_resolver')->nullOnInvalid(), service('security.role_hierarchy')->nullOnInvalid(), service('security.token_storage')->nullOnInvalid(), service('security.authorization_checker')->nullOnInvalid(), ]); $services->alias(OperationAccessCheckerInterface::class, 'sylius.security.operation_access_checker'); }; ================================================ FILE: src/Bundle/Resources/config/services/state/processor/write.php ================================================ services(); $services->set('sylius.state_processor.locator', Processor::class) ->args([tagged_locator('sylius.state_processor')]); $services->set('sylius.state_processor.dispatch_pre_write_event', DispatchPreWriteEventProcessor::class) ->decorate('sylius.state_processor.locator', null, 200) ->args([ service('.inner'), service('sylius.dispatcher.operation'), service('sylius.event_handler.operation'), ]); $services->set('sylius.state_processor.dispatch_post_write_event', DispatchPostWriteEventProcessor::class) ->decorate('sylius.state_processor.locator', null, 200) ->args([ service('.inner'), service('sylius.dispatcher.operation'), service('sylius.event_handler.operation'), ]); $services->set('sylius.state_processor.bulk_aware', BulkAwareProcessor::class) ->decorate('sylius.state_processor.locator', null, 100) ->args([service('.inner')]); }; ================================================ FILE: src/Bundle/Resources/config/services/state/processor.php ================================================ services(); $services->alias('sylius.state_processor.main', 'sylius.state_processor.respond'); $services->set('sylius.state_processor.respond', RespondProcessor::class) ->args([service('sylius.state_responder')]); $services->set('sylius.state_processor.write', WriteProcessor::class) ->decorate('sylius.state_processor.main', null, 100) ->args([ service('.inner'), service('sylius.state_processor.locator'), ]); $services->set('sylius.state_processor.serialize', SerializeProcessor::class) ->decorate('sylius.state_processor.main', null, 200) ->args([ service('.inner'), service('serializer')->nullOnInvalid(), ]); $services->set('sylius.state_processor.flash', FlashProcessor::class) ->decorate('sylius.state_processor.main', null, 300) ->args([ service('.inner'), service('sylius.helper.flash'), ]); }; ================================================ FILE: src/Bundle/Resources/config/services/state/provider.php ================================================ services(); $services->set('sylius.state_provider.read', ReadProvider::class) ->decorate('sylius.state_provider.locator') ->args([service('.inner')]); $services->alias('sylius.state_provider.main', 'sylius.state_provider.read'); $services->set('sylius.state_provider.factory', FactoryProvider::class) ->decorate('sylius.state_provider.read', null, 500) ->args([ service('.inner'), service('sylius.state_factory'), ]); $services->set('sylius.state_provider.dispatch_post_read_event', DispatchPostReadEventProvider::class) ->decorate('sylius.state_provider.read', null, 400) ->args([ service('.inner'), service('sylius.dispatcher.operation'), ]); $services->set('sylius.state_provider.deserialize', DeserializeProvider::class) ->decorate('sylius.state_provider.read', null, 300) ->args([ service('.inner'), service('serializer')->nullOnInvalid(), ]); $services->set('sylius.state_provider.form', FormProvider::class) ->decorate('sylius.state_provider.read', null, 200) ->args([ service('.inner'), service('sylius.form.factory'), ]); $services->set('sylius.state_provider.validate', ValidateProvider::class) ->decorate('sylius.state_provider.read', null, 100) ->args([ service('.inner'), service('validator'), ]); $services->set('sylius.state_provider.security', SecurityProvider::class) ->decorate('sylius.state_provider.read', null, -100) ->args([ service('.inner'), service('sylius.security.operation_access_checker'), ]); }; ================================================ FILE: src/Bundle/Resources/config/services/state.php ================================================ services(); $container->import('state/**/*.php'); $services->set('sylius.resource_factory.expression_language', ExpressionLanguage::class); $services->set('sylius.state_provider.locator', Provider::class) ->args([tagged_locator('sylius.state_provider')]); $services->alias(ProviderInterface::class, 'sylius.state_provider'); $services->set('sylius.state_factory', Factory::class) ->args([ tagged_locator('sylius.resource_factory'), service('sylius.expression_language.argument_parser.factory'), ]); $services->alias(FactoryInterface::class, 'sylius.state_factory'); $services->set('sylius.state_responder', Responder::class) ->args([tagged_locator('sylius.state_responder')]); $services->alias(ResponderInterface::class, 'sylius.state_responder'); $services->set('Sylius\Resource\Symfony\Request\State\Provider') ->args([ tagged_locator('sylius.repository'), service('sylius.repository_argument_resolver.request'), service('sylius.expression_language.argument_parser.repository'), ]) ->tag('sylius.state_provider'); $services->set('Sylius\Resource\StateMachine\State\ApplyStateMachineTransitionProcessor') ->args([ service('sylius.state_machine.operation'), service(PersistProcessor::class)->nullOnInvalid(), ]) ->tag('sylius.state_processor'); $services->set('Sylius\Resource\Symfony\Request\State\Responder') ->args([tagged_locator('sylius.state_responder')]) ->tag('sylius.state_responder'); $services->set('sylius.state_responder.html', TwigResponder::class) ->args([ service('sylius.routing.redirect_handler'), service('sylius.twig.context.factory'), service('twig')->nullOnInvalid(), ]) ->tag('sylius.state_responder'); $services->set('sylius.headers_initiator.api', ApiHeadersInitiator::class); $services->set('sylius.state_responder.api', ApiResponder::class) ->args([service('sylius.headers_initiator.api')]) ->tag('sylius.state_responder'); $services->set('Sylius\Resource\Grid\State\RequestGridProvider') ->args([ service('sylius.grid.view_factory.resource')->nullOnInvalid(), service('sylius.grid.provider')->nullOnInvalid(), ]) ->tag('sylius.state_provider'); }; ================================================ FILE: src/Bundle/Resources/config/services/state_machine.php ================================================ services(); $services->set('sylius.state_machine.operation', OperationStateMachine::class) ->args([tagged_locator('sylius_resource.state_machine', indexAttribute: 'key')]); $services->alias(OperationStateMachineInterface::class, 'sylius.state_machine.operation'); $services->alias('sylius.state_machine.operation.default', 'sylius.state_machine.operation.winzou'); $services->set('sylius.state_machine.operation.symfony', SymfonyOperationStateMachine::class) ->args([service('workflow.registry')->nullOnInvalid()]) ->tag('sylius_resource.state_machine', ['key' => 'symfony']); $services->set('sylius.state_machine.operation.winzou', WinzouOperationStateMachine::class) ->args([service('sm.factory')->nullOnInvalid()]) ->tag('sylius_resource.state_machine', ['key' => 'winzou']); }; ================================================ FILE: src/Bundle/Resources/config/services/storage.php ================================================ services(); $services->defaults() ->public(); $services->set('sylius.storage.session', SessionStorage::class) ->args([service('request_stack')]); $services->alias(SessionStorageInterface::class, 'sylius.storage.session'); $services->set('sylius.storage.cookie', CookieStorage::class) ->tag('kernel.event_subscriber'); $services->alias(CookieStorageInterface::class, 'sylius.storage.cookie'); }; ================================================ FILE: src/Bundle/Resources/config/services/twig.php ================================================ services(); $services->set('sylius.twig.context.factory', ContextFactory::class) ->args([tagged_locator('sylius.twig_context_factory')]); $services->set('sylius.twig.context.factory.default', DefaultContextFactory::class) ->tag('sylius.twig_context_factory'); $services->alias(ContextFactoryInterface::class, 'sylius.twig.context.factory.default'); $services->set('sylius.twig.context.factory.request', RequestContextFactory::class) ->decorate('sylius.twig.context.factory') ->args([service('.inner')]); $services->set('sylius.twig.context.factory.legacy', LegacyContextFactory::class) ->decorate('sylius.twig.context.factory') ->args([service('.inner')]); }; ================================================ FILE: src/Bundle/Resources/config/services.php ================================================ services(); $parameters = $container->parameters(); $container->import('services/console.php'); $container->import('services/context.php'); $container->import('services/controller.php'); $container->import('services/dispatcher.php'); $container->import('services/expression_language.php'); $container->import('services/form.php'); $container->import('services/helper.php'); $container->import('services/listener.php'); $container->import('services/metadata.php'); $container->import('services/routing.php'); $container->import('services/security.php'); $container->import('services/state.php'); $container->import('services/state_machine.php'); $container->import('services/storage.php'); $container->import('services/twig.php'); $parameters->set('sylius.state_machine.class', 'Sylius\Resource\StateMachine\StateMachine'); $services->defaults() ->public(); $services->set('sylius.random_generator', RandomnessGenerator::class); $services->alias(RandomnessGeneratorInterface::class, 'sylius.random_generator'); $services->alias(\Sylius\Component\Resource\Generator\RandomnessGeneratorInterface::class, 'sylius.random_generator') ->deprecate('sylius/resource-bundle', '1.11', 'The "%alias_id%" service alias is deprecated since sylius/resource-bundle 1.11 and will be removed in sylius/resource-bundle 2.0. Use Sylius\Resource\Generator\RandomnessGeneratorInterface instead.'); $services->set('sylius.form.type_extension.form.request_handler', HttpFoundationRequestHandler::class) ->private() ->decorate('form.type_extension.form.request_handler', null, 256); $services->set('sylius.resource_registry', Registry::class) ->private(); $services->alias(RegistryInterface::class, 'sylius.resource_registry') ->private(); $services->alias(\Sylius\Component\Resource\Metadata\RegistryInterface::class, 'sylius.resource_registry') ->private() ->deprecate('sylius/resource-bundle', '1.11', 'The "%alias_id%" service alias is deprecated since sylius/resource-bundle 1.11 and will be removed in sylius/resource-bundle 2.0. Use Sylius\Resource\Metadata\RegistryInterface instead.'); $services->set('sylius.expression_language', BundleExpressionLanguage::class) ->private(); $services->alias(BundleExpressionLanguageInterface::class, 'sylius.expression_language') ->private(); $services->set('sylius.form.extension.type.collection', CollectionTypeExtension::class) ->tag('form.type_extension', ['extended_type' => 'Symfony\Component\Form\Extension\Core\Type\CollectionType']); $services->alias(CollectionTypeExtensionInterface::class, 'sylius.form.extension.type.collection'); $services->set('sylius.form.type.default', DefaultResourceType::class) ->args([ service('sylius.resource_registry'), service('sylius.registry.form_builder'), ]) ->tag('form.type'); $services->alias(DefaultResourceTypeInterface::class, 'sylius.form.type.default'); $services->set('sylius.registry.resource_repository', ServiceRegistry::class) ->private() ->args([ 'Doctrine\Persistence\ObjectRepository', 'resource repository', ]); $services->set('sylius.registry.form_builder', ServiceRegistry::class) ->private() ->args([ 'Sylius\Bundle\ResourceBundle\Form\Builder\DefaultFormBuilderInterface', 'form builder', ]); }; ================================================ FILE: src/Bundle/Resources/config/validation/AbstractTranslation.xml ================================================ ================================================ FILE: src/Bundle/Resources/config/validation/TranslatableInterface.xml ================================================ ================================================ FILE: src/Bundle/Resources/translations/flashes.ar.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: 'تم بنجاح تحديث %resource%.' update: 'تم بنجاح تحديث %resource%.' delete: 'تم بنجاح حذف %resource%.' move: 'وقد تم بنجاح نقل %resource%.' generate: 'تم بنجاح إنشاء %resource%.' revert: 'تم بنجاح حذف %resource%.' restore_deleted: 'تم إسترجاع %resource% بنجاح.' enable: 'تم تمكين %resource% بنجاح.' disable: 'تم تعطيل %resource% بنجاح.' delete_error: 'لا يمكن حذف، %resource% قيد الاستخدام.' race_condition_error: 'لا يمكن تحديث، %resource% تعديل سابقا.' something_went_wrong_error: 'حدث خطأ ما، يرجى المحاولة مرة أخرى.' ================================================ FILE: src/Bundle/Resources/translations/flashes.be.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% быў паспяхова створаны.' update: '%resource% быў паспяхова абноўлены.' delete: '%resource% быў паспяхова выдалены.' ================================================ FILE: src/Bundle/Resources/translations/flashes.bg.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% е създаден успешно.' update: '%resource% е актуализиран успешно.' delete: '%resource% е изтрит успешно.' move: '%resource% е преместен успешно.' generate: '%resource% е успешно генериран.' revert: '%resource% е върнат успешно.' restore_deleted: '%resource% е възстановен успешно.' enable: '%resource% е включен успешно.' disable: '%resource% е изключен успешно.' delete_error: 'Не може да изтриете, %resource% той се използва.' race_condition_error: 'Не може да се актуализира, %resource% е бил променен преди това.' something_went_wrong_error: 'Нещо се обърка, моля, опитайте отново.' ================================================ FILE: src/Bundle/Resources/translations/flashes.cs.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% byl úspěšně vytvořen.' update: '%resource% byl úspěšně aktualizován.' delete: '%resource% byl úspěšně smazán.' bulk_delete: '%resources% byly úspěšně smazány.' move: '%resource% byl úspěšně přesunut.' generate: '% resource%s byly úspěšně vytvořeny.' revert: '%resource% byl úspěšně obnoven.' restore_deleted: '%resource% byl úspěšně obnoven.' enable: '%resource% byl úspěšně povolen.' disable: '%resource% byl úspěšně zakázán.' delete_error: 'Nelze odstranit, %resource% je používán.' race_condition_error: 'Nelze aktualizovat, %resource% byl změněn.' something_went_wrong_error: 'Něco se pokazilo. Zkuste to prosím znovu.' ================================================ FILE: src/Bundle/Resources/translations/flashes.da.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% er blevet oprettet.' update: '%resource% er blevet opdateret.' delete: '%resource% er blevet slettet.' ================================================ FILE: src/Bundle/Resources/translations/flashes.de.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% wurde erfolgreich erstellt.' update: '%resource% wurde erfolgreich aktualisiert.' delete: '%resource% wurde erfolgreich gelöscht.' bulk_delete: '%resources% wurden erfolgreich gelöscht.' move: '%resource% wurde erfolgreich verschoben.' generate: 'Mehrere Elemente (%resource%) wurden erfolgreich generiert.' revert: '%resource% wurden erfolgreich zurückgesetzt.' restore_deleted: '%resource% wurde erfolgreich wiederhergestellt.' enable: '%resource% wurde erfolgreich aktiviert.' disable: '%resource% wurde erfolgreich deaktiviert.' delete_error: 'Löschen fehlgeschlagen, %resource% wird benutzt.' race_condition_error: 'Kann nicht aktualisiert werden, die %resource% wurde zuvor geändert.' something_went_wrong_error: 'Etwas ist schief gelaufen, bitte versuchen Sie es erneut.' ================================================ FILE: src/Bundle/Resources/translations/flashes.de_CH.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% wurde erfolgreich erstellt.' update: '%resource% wurde erfolgreich aktualisiert.' delete: '%resource% wurde erfolgreich gelöscht.' bulk_delete: '%resources% wurden erfolgreich gelöscht.' move: '%resource% wurde erfolgreich verschoben.' generate: 'Mehrere Elemente (%resource%) wurden erfolgreich generiert.' revert: '%resource% wurden erfolgreich zurückgesetzt.' restore_deleted: '%resource% wurde erfolgreich wiederhergestellt.' enable: '%resource% wurde erfolgreich aktiviert.' disable: '%resource% wurde erfolgreich deaktiviert.' delete_error: 'Löschen fehlgeschlagen, %resource% wird benutzt.' race_condition_error: 'Kann nicht aktualisiert werden, die %resource% wurde zuvor geändert.' something_went_wrong_error: 'Etwas ist schief gelaufen, bitte versuchen Sie es erneut.' ================================================ FILE: src/Bundle/Resources/translations/flashes.el.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: 'Το %resource% δημιουργήθηκε με επιτυχία.' update: 'Το %resource% ενημερώθηκε με επιτυχία.' delete: 'Το %resource% διαγράφηκε με επιτυχία.' move: '%resource% μεταφέρθηκε με επιτυχία.' generate: '%resource% δημιουργήθηκε με επιτυχία.' revert: '%resource% έχει επανέλθει με επιτυχία.' restore_deleted: '%resource%. Επιτυχής επαναφορά.' enable: '%resource% ενεργοποιήθηκε με επιτυχία.' disable: '%resource% απενεργοποιήθηκε με επιτυχία.' delete_error: 'Αδύνατη η διαγραφή, %resource% είναι σε χρήση.' race_condition_error: 'Δεν είναι δυνατή η ενημέρωση, %resource% έχει προηγουμένως τροποποιηθεί.' something_went_wrong_error: 'Κάτι πήγε στραβά, παρακαλώ προσπαθήστε ξανά.' ================================================ FILE: src/Bundle/Resources/translations/flashes.en.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% has been successfully created.' update: '%resource% has been successfully updated.' delete: '%resource% has been successfully deleted.' bulk_delete: '%resources% have been successfully deleted.' move: '%resource% has been successfully moved.' generate: '%resource%s have been successfully generated.' revert: '%resource% has been successfully reverted.' restore_deleted: '%resource% has been successfully restored.' enable: '%resource% has been successfully enabled.' disable: '%resource% has been successfully disabled.' delete_error: 'Cannot delete, the %resource% is in use.' race_condition_error: 'Cannot update, the %resource% was previously modified.' something_went_wrong_error: 'Something went wrong, please try again.' ================================================ FILE: src/Bundle/Resources/translations/flashes.es.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: 'El %resource% se ha creado con éxito.' update: 'El %resource% se ha actualizado con éxito.' delete: 'El %resource% se ha eliminado con éxito.' move: 'El %resource% se ha movido con éxito.' generate: 'Los %resource%s se han generado con éxito.' revert: 'El %resource% se ha revertido con éxito.' restore_deleted: 'El %resource% se ha restaurado con éxito.' enable: 'El %resource% se ha activado con éxito.' disable: 'El %resource% se ha desactivado con éxito.' delete_error: 'No se puede eliminar, el %resource% está en uso.' race_condition_error: 'No se puede actualizar, el recurso %resource% fue modificado previamente.' something_went_wrong_error: 'Algo salió mal. Por favor, inténtalo de nuevo.' ================================================ FILE: src/Bundle/Resources/translations/flashes.fa.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% با موفقیت ساخته شد.' update: '%resource% با موفقیت به روز شد.' delete: '%resource% با موفقیت حذف شد.' move: '%resource% با موفقیت منتقل شد.' generate: '%resource% با موفقیت ایجاد شد.' revert: '%resource% با موفقیت به تنظیمات اولیه بازگردانده شد.' restore_deleted: '%resource% با موفقیت بازگردانده شد.' enable: '%resource% با موفقیت فعال شد.' disable: '%resource% با موفقیت غیرفعال شد.' delete_error: 'امکان حذف وجود ندارد. %resource% در حال استفاده می باشد.' ================================================ FILE: src/Bundle/Resources/translations/flashes.fr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% a bien été créé.' update: '%resource% a bien été mis à jour.' delete: '%resource% a bien été supprimé.' bulk_delete: '%resources% ont bien été supprimés.' move: '%resource% a été déplacé avec succès.' generate: '%resource%s ont été générés avec succès.' revert: 'Les modifications de %resource% ont bien été annulées.' restore_deleted: '%resource% a bien été restauré.' enable: '%resource% a bien été activé.' disable: '%resource% a bien été désactivé.' delete_error: 'Impossible de supprimer, %resource% est en cours d''utilisation.' race_condition_error: 'Mise à jour impossible, %resource% a été modifiée précédemment.' something_went_wrong_error: 'Une erreur s''est produite, merci de réessayer.' ================================================ FILE: src/Bundle/Resources/translations/flashes.hr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% je uspješno izvršen.' update: '%resource% je uspješno ažuriran.' delete: '%resource% je uspješno obrisan.' bulk_delete: '%resources% je uspješno obrisan.' move: '%resource% je uspješno pomaknut.' generate: '%resource% je uspješno generiran.' revert: '%resource% je uspješno vraćen.' restore_deleted: '%resource% je uspješno obnovljen.' enable: '%resource% je uspješno omogućen.' disable: '%resource% je uspješno onemogućen.' delete_error: 'Nemoguće izbrisati, %resource% se koristi.' race_condition_error: 'Nije moguće ažurirati, %resource% je prethodno modificiran.' something_went_wrong_error: 'Nešto je pošlo po zlu. Molimo pokušajte ponovno.' ================================================ FILE: src/Bundle/Resources/translations/flashes.hu.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% sikeresen létrehozva.' update: '%resource% sikeresen frissítve.' delete: '%resource% sikeresen törölve.' move: '%resource% sikeresen áthelyezve.' generate: '%resource% sikeresen legenerálva.' revert: '%resource% sikeresen visszavonva.' restore_deleted: '%resource% sikeresen visszaállítva.' enable: '%resource% sikeresen engedélyezve.' disable: '%resource% sikeresen le lett tiltva.' delete_error: 'A %resource% nem törölhető, mert használatban van.' ================================================ FILE: src/Bundle/Resources/translations/flashes.id.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% telah berhasil dibuat.' update: '%resource% telah berhasil diupdate.' delete: '%resource% telah berhasil dihapus.' move: '%resource% telah berhasil dipindahkan.' generate: '%resource% s telah berhasil dihasilkan.' revert: '%resource% telah berhasil dikembalikan.' restore_deleted: '%resource% telah berhasil dipulihkan.' enable: '%resource% telah berhasil diaktifkan.' disable: '%resource% telah berhasil dinonaktifkan.' delete_error: 'Tidak dapat menghapus,%resource% sedang digunakan.' race_condition_error: 'Tidak dapat diperbarui,%resource% sebelumnya telah dimodifikasi.' something_went_wrong_error: 'Ada yang tidak beres, coba lagi.' ================================================ FILE: src/Bundle/Resources/translations/flashes.it.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% è stato creato correttamente.' update: '%resource% è stato aggiornato con successo.' delete: '%resource% è stato eliminato correttamente.' move: '%resource% è stato spostato correttamente.' generate: '% resource%s sono stati generati correttamente.' revert: '%resource% è stato resettato correttamente.' restore_deleted: '%resource% è stato ripristinato con successo.' enable: '%resource% è stato attivato con successo.' disable: '%resource% è stato disattivato con successo.' delete_error: 'Non è possibile eliminare, il %resource% è in uso.' race_condition_error: 'Non è possibile aggiornare, il %resource% è stato modificato in precedenza.' something_went_wrong_error: 'Qualcosa è andato storto. Ti preghiamo di riprovare.' ================================================ FILE: src/Bundle/Resources/translations/flashes.ja.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource%の登録が完了しました。' update: '%resource%の更新が完了しました。' delete: '%resource%の削除が完了しました。' ================================================ FILE: src/Bundle/Resources/translations/flashes.lt.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% sėkmingai sukurtas.' update: '%resource% sėkmingai atnaujintas.' delete: '%resource% sėkmingai pašalintas.' move: '%resource% perkeltas sėkmingai.' generate: '% resource%s sėkmingai sugeneruotas.' revert: '%resource% sėkmingai atstatytas.' restore_deleted: '%resource% sėkmingai atstatytas.' enable: '%resource% sėkmingai įgalintas.' disable: '%resource% sėkmingai išjungtas.' delete_error: 'Negalite panaikinti, nes %resource% yra naudojamas.' ================================================ FILE: src/Bundle/Resources/translations/flashes.nl.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% is succesvol aangemaakt.' update: '%resource% is succesvol bijgewerkt.' delete: '%resource% is succesvol verwijderd.' bulk_delete: '%resources% zijn succesvol verwijderd.' move: '%resource% is verplaatst.' generate: '% resource%s zijn aangemaakt.' revert: '%resource% is met succes hersteld.' restore_deleted: '%resource% is succesvol hersteld.' enable: '%resource% is ingeschakeld.' disable: '%resource% is uitgeschakeld.' delete_error: 'Verwijderen niet mogelijk, %resource% is nog steeds in gebruik.' race_condition_error: 'Kan niet geüpdate worden, de %resource% is eerder bewerkt.' something_went_wrong_error: 'Er is een fout opgetreden, probeer het nogmaals.' ================================================ FILE: src/Bundle/Resources/translations/flashes.no.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% er opprettet.' update: '%resource% er oppdatert.' delete: '%resource% er slettet.' move: '%resource% har blitt flyttet.' generate: '%resource%s har blitt generert.' revert: '%resource% har blitt tilbakestilt.' restore_deleted: '%resource% har blitt gjenopprettet.' enable: '%resource% er aktivert.' disable: '%resource% er deaktivert.' delete_error: 'Kan ikke slette, %resource% er i bruk.' race_condition_error: 'Kan ikke oppdatere, %resource% ble endret.' something_went_wrong_error: 'Det har oppstått en feil. Vennligst prøv igjen.' ================================================ FILE: src/Bundle/Resources/translations/flashes.pl.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% został pomyślnie utworzony.' update: '%resource% został pomyślnie uaktualniony.' delete: '%resource% został pomyślnie usunięty.' move: '%resource% został pomyślnie przeniesiony.' generate: '%resource%s zostały pomyślnie wygenerowane.' revert: '%resource% został pomyślnie przywrócony.' restore_deleted: '%resource% został pomyślnie odzyskany.' enable: '%resource% został pomyślnie włączony.' disable: '%resource% został pomyślnie wyłączony.' delete_error: 'Nie można usunąć, %resource% jest w użyciu.' race_condition_error: 'Nie można uaktualnić, %resource% został już wcześniej zmodyfikowany.' something_went_wrong_error: 'Coś poszło nie tak, spróbuj ponownie.' ================================================ FILE: src/Bundle/Resources/translations/flashes.pt.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% foi criado com sucesso.' update: '%resource% foi atualizado com sucesso.' delete: '%resource% foi eliminado com sucesso.' move: '%resource% foi movido com sucesso.' generate: '%resource%s foi/foram criado(s) com sucesso.' revert: '%resource% foi revertido com sucesso.' restore_deleted: '%resource% foi restaurado com sucesso.' enable: '%resource% foi ativado com sucesso.' disable: '%resource% foi desativado com sucesso.' delete_error: 'Não é possível eliminar, o %resource% está em uso.' ================================================ FILE: src/Bundle/Resources/translations/flashes.pt_BR.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% foi criado com sucesso.' update: '%resource% foi atualizado com sucesso.' delete: '%resource% foi excluído com sucesso.' move: '%resource% foi movida com sucesso.' generate: '%recurso%s %fo%ram %gerado%s com sucesso.' revert: '%resource% foi revertido com sucesso.' restore_deleted: '%resource% foi restaurado com sucesso.' enable: '%resource% foi ativado com sucesso.' disable: '%resource% foi desativado com sucesso.' delete_error: 'Não é possível excluir, o %resource% está em uso.' race_condition_error: 'Não é possível atualizar, o %resource% foi previamente modificado.' something_went_wrong_error: 'Algo deu errado, tente de novo.' ================================================ FILE: src/Bundle/Resources/translations/flashes.ro.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% a fost creată cu succes.' update: '%resource% a fost actualizată cu succes.' delete: '%resource% a fost ștearsă cu succes.' move: '%resource% a fost mutat cu succes.' generate: '%resource% a fost generat cu succes.' revert: '%resource% a revenit la starea inițială cu succes.' restore_deleted: '%resource% a revenit la starea inițială cu succes.' enable: '%resource% a fost activat cu succes.' disable: '%resource% a fost dezactivat cu succes.' delete_error: 'Nu poate fi șters, %resource% este utilizat.' race_condition_error: 'Nu poate fi actualizat, %resource% a fost modificat anterior.' something_went_wrong_error: 'A apărut o problemă. Te rugăm să mai încerci o dată.' ================================================ FILE: src/Bundle/Resources/translations/flashes.ru.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% успешно создан.' update: '%resource% успешно изменен.' delete: '%resource% удален успешно.' move: '%resource% успешно перемещен.' generate: '% resource%ы созданы успешно.' revert: '%resource% успешно откачен.' restore_deleted: '%resource% успешно восстановлен.' enable: '%resource% успешно включен.' disable: '%resource% успешно отключен.' delete_error: 'Не удается удалить %resource%, т. к. он используется.' race_condition_error: 'Не удается обновить, %resource% был изменен ранее.' something_went_wrong_error: 'Что-то пошло не так. Попробуйте еще раз.' ================================================ FILE: src/Bundle/Resources/translations/flashes.sk.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% bol úspešne vytvorený.' update: '%resource% bol úspešne aktualizovaný.' delete: '%resource% bol úspešne odstránený.' bulk_delete: '%resources% bol úspešne odstránený.' move: '%resource% bol úspešne premiestnený.' generate: '% resource% boli úspešne vytvorené.' revert: '%resource% bol úspešne navrátený.' restore_deleted: '%resource% bol úspešne obnovený.' enable: '%resource% bol úspešne aktivovaný.' disable: '%resource% bol úspešne deaktivovaný.' delete_error: 'Nie je možné odstrániť, %resource% sa používa.' race_condition_error: 'Nemôžete aktualizovať, %resource% bol zmenený.' something_went_wrong_error: 'Niečo sa pokazilo, skúste to prosím znova.' ================================================ FILE: src/Bundle/Resources/translations/flashes.sl.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% je bil uspešno ustvarjen.' update: '%resource% je bil uspešno posodobljen.' delete: '%resource% je bil uspešno izbrisan.' move: '%resource% je bil uspešno premaknjen.' generate: '%resource% so bili uspešno generirani.' revert: '%resource% je bila uspešno povrnjen.' restore_deleted: '%resource% je bil uspešno obnovljen.' enable: '%resource% je bil uspešno omogočen.' disable: '%resource% je bil uspešno onemogočen.' ================================================ FILE: src/Bundle/Resources/translations/flashes.sq.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% u krijua me sukses.' update: '%resource% u përditësua me sukses.' delete: '%resource% u fshi me sukses.' move: '%resource% u zhvendos me sukses.' generate: '%resource%s u gjenerua me sukses.' revert: '%resource% u anulua me sukses.' restore_deleted: '%resource% u rivendos me sukses.' enable: '%resource% u aktivizua me sukses.' disable: '%resource% u çaktivizua me sukses.' delete_error: 'Nuk mund te fshihet, %resource% eshte ne perdorim.' ================================================ FILE: src/Bundle/Resources/translations/flashes.sr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% је успешно креиран.' update: '%resource% је успешно ажуриран.' delete: '%resource% је успешно обрисан.' ================================================ FILE: src/Bundle/Resources/translations/flashes.sr_CS.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% je uspešno kreiran.' update: '%resource% je uspešno ažuriran.' delete: '%resource% je uspešno obrisan.' ================================================ FILE: src/Bundle/Resources/translations/flashes.th.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% ถูกสร้างเรียบร้อยแล้ว' update: '%resource% ถูกปรับปรุงเรียบร้อยแล้ว' delete: '%resource% ถูกลบทิ้งแล้ว' move: '%resource% ถูกลบทิ้งแล้ว' generate: '%resource% ถูกสร้างเรียบร้อยแล้ว' revert: '%resource% ถูกเรียกกลับมาเรียบร้อยแล้ว' restore_deleted: '%resource% ถูกกู้คืนเรียบร้อยแล้ว' enable: '%resource% มีการเปิดใช้เรียบร้อยแล้ว' disable: '%resource% ได้ถูกปิดใช้งานเรียบร้อยแล้ว' delete_error: 'ไม่สามารถลบ %resource% ขณะใช้งาน' ================================================ FILE: src/Bundle/Resources/translations/flashes.tr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% başarılı olarak oluşturuldu.' update: '%resource% başarılı olarak güncellendi.' delete: '%resource% başarılı olarak silindi.' move: '%resource% başarıyla taşındı.' generate: '% resource%s başarıyla oluşturdu.' revert: '%resource% başarıyla döndürüldü.' restore_deleted: '%resource% başarılı olarak silindi.' enable: '%resource% başarıyla etkinleştirildi.' disable: '%resource% başarıyla devre dışı bırakıldı.' delete_error: 'Silinemez,%resource%kullanılıyor.' race_condition_error: 'Güncellenemez,%resource% önceden değiştirilmiş.' something_went_wrong_error: 'Birşeyler ters gitti, lütfen tekrar deneyin.' ================================================ FILE: src/Bundle/Resources/translations/flashes.uk.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% був успішно створений.' update: '%resource% був успішно оновлений.' delete: '%resource% був успішно видалений.' move: '%resource% був успішно переміщений.' generate: '%resource%и були успішно згенеровані.' revert: '%resource% був успішно повернутий.' restore_deleted: '%resource% був успішно відновлений.' enable: '%resource% був успішно ввімкнений.' disable: '%resource% був успішно вимкнений.' delete_error: 'Неможливо видалити, %resource% знаходиться у використанні.' race_condition_error: 'Не вдається оновити, %resource% був змінений раніше.' something_went_wrong_error: 'Щось пішло не так. Будь ласка, спробуйте ще раз.' ================================================ FILE: src/Bundle/Resources/translations/flashes.zh_CN.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: create: '%resource% 创建成功。' update: '%resource% 更新成功。' delete: '%resource% 删除成功。' move: '%resource% 移动成功。' generate: '%resource%s 已生成成功。' revert: '%resource% 已经被成功地还原。' restore_deleted: '%resource% 已经被成功地还原。' enable: '%resource% 已成功启用。' disable: '%resource% 已被成功地禁用。' delete_error: '不能删除,%resource% 正在使用中。' race_condition_error: '%resource% 已经被修改,不能更新。' something_went_wrong_error: '出现错误。请重试。' ================================================ FILE: src/Bundle/Resources/translations/messages.ar.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: اضافة delete: حذف ================================================ FILE: src/Bundle/Resources/translations/messages.be.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Дадаць delete: Выдаліць ================================================ FILE: src/Bundle/Resources/translations/messages.bg.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Добави delete: Изтрий ================================================ FILE: src/Bundle/Resources/translations/messages.ca.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: delete: Suprimir ================================================ FILE: src/Bundle/Resources/translations/messages.cs.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Přidat delete: Odstranit ================================================ FILE: src/Bundle/Resources/translations/messages.da.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Tilføj delete: Slet ================================================ FILE: src/Bundle/Resources/translations/messages.de.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Hinzufügen delete: Löschen ================================================ FILE: src/Bundle/Resources/translations/messages.de_CH.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Hinzufügen delete: Löschen ================================================ FILE: src/Bundle/Resources/translations/messages.el.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Προσθήκη delete: Διαγραφή ================================================ FILE: src/Bundle/Resources/translations/messages.en.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Add delete: Delete ================================================ FILE: src/Bundle/Resources/translations/messages.es.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Añadir delete: Eliminar ================================================ FILE: src/Bundle/Resources/translations/messages.fa.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: ﺍﻓﺰﻭﺩﻥ delete: حذف ================================================ FILE: src/Bundle/Resources/translations/messages.fi.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Lisää delete: Poista ================================================ FILE: src/Bundle/Resources/translations/messages.fr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Ajouter delete: Supprimer ================================================ FILE: src/Bundle/Resources/translations/messages.he.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: הוסף delete: מחק ================================================ FILE: src/Bundle/Resources/translations/messages.hr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Dodaj delete: sylius.account.address.action.delete ================================================ FILE: src/Bundle/Resources/translations/messages.hu.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Hozzáadás delete: Törlés ================================================ FILE: src/Bundle/Resources/translations/messages.id.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Tambah delete: Menghapus ================================================ FILE: src/Bundle/Resources/translations/messages.it.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Aggiungi delete: Cancella ================================================ FILE: src/Bundle/Resources/translations/messages.ja.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: delete: sylius.account.address.action.delete ================================================ FILE: src/Bundle/Resources/translations/messages.lt.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Pridėti delete: Šalinti ================================================ FILE: src/Bundle/Resources/translations/messages.nl.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Toevoegen delete: Verwijderen ================================================ FILE: src/Bundle/Resources/translations/messages.no.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Legg til delete: Slett ================================================ FILE: src/Bundle/Resources/translations/messages.pl.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Dodaj delete: Usuń ================================================ FILE: src/Bundle/Resources/translations/messages.pt.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Adicionar delete: Eliminar ================================================ FILE: src/Bundle/Resources/translations/messages.pt_BR.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Adicionar delete: Excluir ================================================ FILE: src/Bundle/Resources/translations/messages.ro.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Adăugați delete: Șterge ================================================ FILE: src/Bundle/Resources/translations/messages.ru.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Добавить delete: Удалить ================================================ FILE: src/Bundle/Resources/translations/messages.sk.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Pridať delete: Odstrániť ================================================ FILE: src/Bundle/Resources/translations/messages.sl.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Dodaj delete: Zbriši ================================================ FILE: src/Bundle/Resources/translations/messages.sq.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Shto delete: Fshi ================================================ FILE: src/Bundle/Resources/translations/messages.sr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: delete: Обриши ================================================ FILE: src/Bundle/Resources/translations/messages.sr_CS.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Dodaj delete: Obrisati ================================================ FILE: src/Bundle/Resources/translations/messages.sv.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Lägg tilll delete: Radera ================================================ FILE: src/Bundle/Resources/translations/messages.th.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: เพิ่ม delete: ลบ ================================================ FILE: src/Bundle/Resources/translations/messages.tr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Ekle delete: Sil ================================================ FILE: src/Bundle/Resources/translations/messages.uk.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: Додати delete: Видалити ================================================ FILE: src/Bundle/Resources/translations/messages.zh_CN.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: 新增 delete: 删除 ================================================ FILE: src/Bundle/Resources/translations/messages.zh_TW.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: form: collection: add: 新增 ================================================ FILE: src/Bundle/Resources/translations/validators.ar.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "الموارد المقدمة معطلة" not_disabled: "الموارد المقدمة مفعلة" ================================================ FILE: src/Bundle/Resources/translations/validators.bg.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Този ресурс е изключен" not_disabled: "Този ресурс е включен" ================================================ FILE: src/Bundle/Resources/translations/validators.cs.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Uvedený prostředek je zakázán" not_disabled: "Uvedený prostředek je povolen" ================================================ FILE: src/Bundle/Resources/translations/validators.da.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Den valgte ressource er deaktiveret" not_disabled: "Den valgte ressource er aktiveret" ================================================ FILE: src/Bundle/Resources/translations/validators.de.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Gewählte Ressource ist deaktiviert" not_disabled: "Gewählte Ressource ist aktiviert" ================================================ FILE: src/Bundle/Resources/translations/validators.de_CH.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Gewählte Ressource ist deaktiviert" not_disabled: "Gewählte Ressource ist aktiviert" ================================================ FILE: src/Bundle/Resources/translations/validators.el.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Το συγκεκριμένο αντικείμενο είναι απενεργοποιημένο" not_disabled: "Το συγκεκριμένο αντικείμενο είναι ενεργοποιημένο" ================================================ FILE: src/Bundle/Resources/translations/validators.en.yml ================================================ sylius: resource: not_enabled: "Given resource is disabled" not_disabled: "Given resource is enabled" translation: locale: not_blank: Please enter the locale. invalid: This value is not a valid locale. unique: A translation for the {{ value }} locale code already exists. ================================================ FILE: src/Bundle/Resources/translations/validators.es.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "El recurso indicado está desactivado" not_disabled: "El recurso está activado" ================================================ FILE: src/Bundle/Resources/translations/validators.fa.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "منبع تعریف شده، غیر فعال گردیده" not_disabled: "منبع تعریف شده فعال گردید" ================================================ FILE: src/Bundle/Resources/translations/validators.fr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "La ressource spécifiée est désactivée" not_disabled: "La ressource spécifiée est activée" ================================================ FILE: src/Bundle/Resources/translations/validators.he.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "המשאב אינו זמין" not_disabled: "המשאב זמין כעת" ================================================ FILE: src/Bundle/Resources/translations/validators.hr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Resurs nije omogućen" not_disabled: "Resurs je omogućen" ================================================ FILE: src/Bundle/Resources/translations/validators.hu.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Az adott erőforrás le van tiltva" not_disabled: "Az adott erőforrás engedélyezve van" ================================================ FILE: src/Bundle/Resources/translations/validators.id.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Sumber daya yang diberikan dinonaktifkan" not_disabled: "Sumber daya yang diberikan diaktifkan" ================================================ FILE: src/Bundle/Resources/translations/validators.it.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "La data risorsa è disattivata" not_disabled: "La data risorsa è abilitata" ================================================ FILE: src/Bundle/Resources/translations/validators.lt.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Resursas yra išjungtas" not_disabled: "Resursas yra įjungtas" ================================================ FILE: src/Bundle/Resources/translations/validators.nl.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Deze resource is uitgeschakeld" not_disabled: "Deze resource is ingeschakeld" ================================================ FILE: src/Bundle/Resources/translations/validators.pl.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Podany zasób jest wyłączony" not_disabled: "Podany zasób jest włączony" ================================================ FILE: src/Bundle/Resources/translations/validators.pt.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "O recurso está desactivado" not_disabled: "O recurso está desativado" ================================================ FILE: src/Bundle/Resources/translations/validators.pt_BR.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "O recurso fornecido está desativado" not_disabled: "O recurso fornecido está ativado" ================================================ FILE: src/Bundle/Resources/translations/validators.ro.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Resursa este dezactivată" not_disabled: "Resursa este activată" ================================================ FILE: src/Bundle/Resources/translations/validators.ru.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Данный ресурс отключен" not_disabled: "Данный ресурс включен" ================================================ FILE: src/Bundle/Resources/translations/validators.sk.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Zdroj je vypnutý" not_disabled: "Zdroj je zapnutý" ================================================ FILE: src/Bundle/Resources/translations/validators.sl.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Dani vir je onemogočen" not_disabled: "Dani vir je omogočen" ================================================ FILE: src/Bundle/Resources/translations/validators.sq.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Burimi i zgjedhur është i çaktivizuar" not_disabled: "Burimi i zgjedhur është aktivizuar" ================================================ FILE: src/Bundle/Resources/translations/validators.sr_CS.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Traženi resurs je isključen" not_disabled: "Resurs je omogućen" ================================================ FILE: src/Bundle/Resources/translations/validators.th.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "ข้อมูลถูกปิดการใช้งาน" not_disabled: "ข้อมูลถูกเปิดการใช้งาน" ================================================ FILE: src/Bundle/Resources/translations/validators.tr.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Verilen kaynak devre dışı" not_disabled: "Verilen kaynak etkin" ================================================ FILE: src/Bundle/Resources/translations/validators.uk.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "Даний ресурс вимкнений" not_disabled: "Даний ресурс увімкнений" ================================================ FILE: src/Bundle/Resources/translations/validators.zh_CN.yml ================================================ # This file is part of the Sylius package. # (c) Sylius Sp. z o.o. sylius: resource: not_enabled: "该资源已被禁用" not_disabled: "该资源已启用" ================================================ FILE: src/Bundle/Resources/views/Macros/actions.html.twig ================================================ {% macro create(url) %}
{{ 'sylius.ui.cancel'|trans }}
{% endmacro %} {% macro update(url) %}
{{ 'sylius.ui.cancel'|trans }}
{% endmacro %} ================================================ FILE: src/Bundle/Resources/views/Macros/buttons.html.twig ================================================ {% macro show(url, message) %} {{ message is empty ? 'sylius.ui.details'|trans : message }} {% endmacro %} {% macro generic(url, message, icon) %} {% if icon is not empty %}{% endif %}{{ message }} {% endmacro %} {% macro create(url, message) %} {{ message is empty ? 'sylius.ui.create'|trans : message }} {% endmacro %} {% macro edit(url, message) %} {{ message is empty ? 'sylius.ui.edit'|trans : message }} {% endmacro %} {% macro delete(url, message, disabled=false, modal=true) %} {% if disabled %} {{ message is empty ? 'sylius.ui.delete'|trans : message }} {% else %}
{% endif %} {% endmacro %} {% macro manage(url, message) %} {{ message is empty ? 'sylius.ui.manage'|trans : message }} {% endmacro %} {% macro move(url, direction, first=false, last=false, message='') %}
{% endmacro %} {% macro patch(url, message, icon, button) %}
{% endmacro %} {% macro enable(url, message, icon, button) %}
{% endmacro %} {% macro disable(url, message, icon, button) %}
{% endmacro %} ================================================ FILE: src/Bundle/Resources/views/Macros/notification.html.twig ================================================ {# Display all the flashes messages #} {% macro flashes() %} {% for type, flashes in app.session.flashBag.all %}
    {% for flash in flashes %}
  • {{ flash }}
  • {% endfor %}
{% endfor %} {% endmacro %} {# Display a bootstrap alert : - text : Message to print (string or array) - type : danger | warning | success | info (Default : info) #} {% macro alert(text, type) %} {% set type = 'alert-' ~ type|default('info') %} {% if text|default %}
{% if text is iterable %}
    {% for line in text %}
  • {{ line.message|trans(line.messageParameters) }}

  • {% endfor %}
{% else %} {{ text|trans }} {% endif %}
{% endif %} {% endmacro %} {% macro danger(text) %} {% from _self import alert %} {{ alert(text, 'danger') }} {% endmacro %} {% macro error(text) %} {% from _self import danger %} {{ danger(text) }} {% endmacro %} {% macro warning(text) %} {% from _self import alert %} {{ alert(text, 'warning') }} {% endmacro %} {% macro success(text) %} {% from _self import alert %} {{ alert(text, 'success') }} {% endmacro %} {% macro info(text) %} {% from _self import alert %} {{ alert(text, 'info') }} {% endmacro %} ================================================ FILE: src/Bundle/Resources/views/Twig/paginate.html.twig ================================================ {{ paginator.maxPerPage }}  / {{ paginator.nbResults }} ================================================ FILE: src/Bundle/Resources/views/Twig/sorting.html.twig ================================================ {{ label }} {% if icon -%} {%- if currentOrder == 'desc' -%} {%- else -%} {%- endif %} {%- endif %} ================================================ FILE: src/Bundle/Resources/views/forms.html.twig ================================================ {% block collection_widget -%} {% from '@SyliusResource/Macros/notification.html.twig' import error %} {% set attr = attr|merge({'class': attr.class|default ~ ' controls collection-widget'}) %} {% apply spaceless %}
{{ error(form.vars.errors) }} {% if prototypes|default is iterable %} {% for key, subPrototype in prototypes %} {% endfor %} {% endif %}
{% for child in form %} {{ _self.collectionItem(child, allow_delete, button_delete_label, loop.index0) }} {% endfor %}
{% if prototype is defined and allow_add %} {{ button_add_label|trans }} {% endif %}
{% endapply %} {%- endblock collection_widget %} {% macro collectionItem(form, allow_delete, button_delete_label, index) %} {% apply spaceless %}
{% if allow_delete %}

{{ button_delete_label|trans }}

{% endif %} {% if not form.children|length %} {{ form_widget(form) }} {% else %} {{ form_rest(form) }} {% endif %}
{% endapply %} {% endmacro %} ================================================ FILE: src/Bundle/Routing/Configuration.php ================================================ */ public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('routing'); /** @var ArrayNodeDefinition> $rootNode */ $rootNode = $treeBuilder->getRootNode(); $rootNode ->children() ->scalarNode('alias')->isRequired()->cannotBeEmpty()->end() ->scalarNode('path')->defaultValue(null)->end() ->scalarNode('identifier')->defaultValue('id')->end() ->arrayNode('criteria') ->useAttributeAsKey('identifier') ->scalarPrototype() ->end() ->end() ->booleanNode('filterable')->end() ->variableNode('form')->cannotBeEmpty()->end() ->scalarNode('serialization_version')->cannotBeEmpty()->end() ->scalarNode('section')->cannotBeEmpty()->end() ->scalarNode('redirect')->cannotBeEmpty()->end() ->scalarNode('templates')->cannotBeEmpty()->end() ->scalarNode('grid')->cannotBeEmpty()->end() ->booleanNode('permission')->defaultValue(false)->end() ->scalarNode('condition')->cannotBeEmpty()->end() ->arrayNode('except') ->scalarPrototype()->end() ->end() ->arrayNode('only') ->scalarPrototype()->end() ->end() ->variableNode('vars')->cannotBeEmpty()->end() ->end() ; return $treeBuilder; } } ================================================ FILE: src/Bundle/Routing/CrudRoutesAttributesLoader.php ================================================ mapping = $mapping; $this->resourceLoader = $resourceLoader; } public function __invoke(): RouteCollection { $routeCollection = new RouteCollection(); $paths = $this->mapping['paths'] ?? []; foreach (ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories($paths) as $reflectionClass) { $className = $reflectionClass->getName(); $this->addRoutesForSyliusCrudRoutesAttributes($routeCollection, $className); } return $routeCollection; } /** * @param class-string $className */ private function addRoutesForSyliusCrudRoutesAttributes(RouteCollection $routeCollection, string $className): void { $attributes = ClassReflection::getClassAttributes($className, SyliusCrudRoutes::class); $attributes = array_merge($attributes, ClassReflection::getClassAttributes($className, LegacySyliusCrudRoutes::class)); foreach ($attributes as $reflectionAttribute) { $resource = Yaml::dump($reflectionAttribute->getArguments()); $resourceRouteCollection = $this->resourceLoader->load($resource); $routeCollection->addCollection($resourceRouteCollection); } } } ================================================ FILE: src/Bundle/Routing/ResourceLoader.php ================================================ inflector = $inflector ?? new Inflector(); } public function load($resource, $type = null): RouteCollection { $processor = new Processor(); $configurationDefinition = new Configuration(); $configuration = Yaml::parse($resource); $configuration = $processor->processConfiguration($configurationDefinition, ['routing' => $configuration]); if (!empty($configuration['only']) && !empty($configuration['except'])) { throw new \InvalidArgumentException('You can configure only one of "except" & "only" options.'); } $routesToGenerate = ['show', 'index', 'create', 'update', 'delete', 'bulkDelete']; if (!empty($configuration['only'])) { $routesToGenerate = $configuration['only']; } if (!empty($configuration['except'])) { $routesToGenerate = array_diff($routesToGenerate, $configuration['except']); } $isApi = $type === 'sylius.resource_api'; /** @var MetadataInterface $metadata */ $metadata = $this->resourceRegistry->get($configuration['alias']); $routes = $this->routeFactory->createRouteCollection(); $rootPath = $configuration['path'] ?? $this->getRootPath($metadata->getPluralName()); $identifier = sprintf('{%s}', $configuration['identifier']); /** @var bool $bcLayerEnabled */ $bcLayerEnabled = $this->routingPathBcLayer ?? true; $trailingSlash = $bcLayerEnabled ? '/' : ''; if (in_array('index', $routesToGenerate, true)) { $indexRoute = $this->createRoute($metadata, $configuration, $rootPath . $trailingSlash, 'index', ['GET'], $isApi); $routes->add($this->getRouteName($metadata, $configuration, 'index'), $indexRoute); } if (in_array('create', $routesToGenerate, true)) { $createRoute = $this->createRoute($metadata, $configuration, $isApi ? $rootPath . $trailingSlash : $rootPath . '/new', 'create', $isApi ? ['POST'] : ['GET', 'POST'], $isApi); $routes->add($this->getRouteName($metadata, $configuration, 'create'), $createRoute); } if (in_array('update', $routesToGenerate, true)) { $httpMethods = ['GET', 'PUT', 'PATCH']; if (!$bcLayerEnabled) { $httpMethods[] = 'POST'; } $updateRoute = $this->createRoute($metadata, $configuration, $isApi ? $rootPath . '/' . $identifier : $rootPath . '/' . $identifier . '/edit', 'update', $isApi ? ['PUT', 'PATCH'] : $httpMethods, $isApi); $routes->add($this->getRouteName($metadata, $configuration, 'update'), $updateRoute); } if (in_array('show', $routesToGenerate, true)) { $showRoute = $this->createRoute($metadata, $configuration, $rootPath . '/' . $identifier, 'show', ['GET'], $isApi); $routes->add($this->getRouteName($metadata, $configuration, 'show'), $showRoute); } if (!$isApi && in_array('bulkDelete', $routesToGenerate, true)) { $httpMethods = ['DELETE']; if (!$bcLayerEnabled) { $httpMethods[] = 'POST'; } $bulkDeleteRoute = $this->createRoute($metadata, $configuration, $rootPath . '/' . 'bulk-delete', 'bulkDelete', $httpMethods, $isApi); $routes->add($this->getRouteName($metadata, $configuration, 'bulk_delete'), $bulkDeleteRoute); } if (in_array('delete', $routesToGenerate, true)) { $httpMethods = ['DELETE']; if (!$bcLayerEnabled) { $httpMethods[] = 'POST'; } $deleteRoute = $this->createRoute($metadata, $configuration, $isApi ? $rootPath . '/' . $identifier : $rootPath . '/' . $identifier . ($bcLayerEnabled ? '' : '/delete'), 'delete', $isApi ? ['DELETE'] : $httpMethods, $isApi); $routes->add($this->getRouteName($metadata, $configuration, 'delete'), $deleteRoute); } return $routes; } public function supports($resource, $type = null): bool { return 'sylius.resource' === $type || 'sylius.resource_api' === $type; } private function getRootPath(string $pluralName): string { if ($this->routingPathBcLayer) { if (!class_exists(Urlizer::class) || !class_exists(Transliterator::class)) { throw new RuntimeException('Cannot use the routing bc-layer when the "behat/transliterator" package is not installed. Try to disable the routing path bc-layer in the Sylius Resource Bundle configuration using "sylius_resource.routing_path_bc_layer: false"'); } return sprintf('/%s', Urlizer::urlize($pluralName)); } return $this->inflector->dashize($pluralName); } private function createRoute( MetadataInterface $metadata, array $configuration, string $path, string $actionName, array $methods, bool $isApi = false, ): Route { $defaults = [ '_controller' => $metadata->getServiceId('controller') . sprintf('::%sAction', $actionName), ]; if ($isApi && 'index' === $actionName) { $defaults['_sylius']['serialization_groups'] = ['Default']; } if ($isApi && in_array($actionName, ['show', 'create', 'update'], true)) { $defaults['_sylius']['serialization_groups'] = ['Default', 'Detailed']; } if ($isApi && 'delete' === $actionName) { $defaults['_sylius']['csrf_protection'] = false; } if (isset($configuration['grid']) && 'index' === $actionName) { $defaults['_sylius']['grid'] = $configuration['grid']; } if (isset($configuration['form']) && in_array($actionName, ['create', 'update'], true)) { $defaults['_sylius']['form'] = $configuration['form']; } if (isset($configuration['serialization_version'])) { $defaults['_sylius']['serialization_version'] = $configuration['serialization_version']; } if (isset($configuration['section'])) { $defaults['_sylius']['section'] = $configuration['section']; } if (!empty($configuration['criteria'])) { $defaults['_sylius']['criteria'] = $configuration['criteria']; } if (array_key_exists('filterable', $configuration)) { $defaults['_sylius']['filterable'] = $configuration['filterable']; } if (isset($configuration['templates']) && in_array($actionName, ['show', 'index', 'create', 'update'], true)) { $defaults['_sylius']['template'] = sprintf( false === strpos($configuration['templates'], ':') ? '%s/%s.html.twig' : '%s:%s.html.twig', $configuration['templates'], $actionName, ); } if (isset($configuration['redirect']) && in_array($actionName, ['create', 'update'], true)) { $defaults['_sylius']['redirect'] = $this->getRouteName($metadata, $configuration, $configuration['redirect']); } if (isset($configuration['permission'])) { $defaults['_sylius']['permission'] = $configuration['permission']; } if (isset($configuration['vars']['all'])) { $defaults['_sylius']['vars'] = $configuration['vars']['all']; } if (isset($configuration['vars'][$actionName])) { $vars = $configuration['vars']['all'] ?? []; $defaults['_sylius']['vars'] = array_merge($vars, $configuration['vars'][$actionName]); } if ($actionName === 'bulkDelete') { $defaults['_sylius']['paginate'] = false; $defaults['_sylius']['repository'] = [ 'method' => 'findById', 'arguments' => ['$ids'], ]; } $condition = $configuration['condition'] ?? ''; return $this->routeFactory->createRoute($path, $defaults, [], [], '', [], $methods, $condition); } private function getRouteName(MetadataInterface $metadata, array $configuration, string $actionName): string { $sectionPrefix = isset($configuration['section']) ? $configuration['section'] . '_' : ''; return sprintf('%s_%s%s_%s', $metadata->getApplicationName(), $sectionPrefix, $metadata->getName(), $actionName); } } ================================================ FILE: src/Bundle/Routing/RouteAttributesFactory.php ================================================ getArguments(); Assert::keyExists($arguments, 'name', 'Your route should have a name attribute.'); $syliusOptions = []; if (isset($arguments['template'])) { $syliusOptions['template'] = $arguments['template']; } if (isset($arguments['vars'])) { $syliusOptions['vars'] = $arguments['vars']; } if (isset($arguments['criteria'])) { $syliusOptions['criteria'] = $arguments['criteria']; } if (isset($arguments['repository'])) { $syliusOptions['repository'] = $arguments['repository']; } if (isset($arguments['serializationGroups'])) { $syliusOptions['serialization_groups'] = $arguments['serializationGroups']; } if (isset($arguments['serializationVersion'])) { $syliusOptions['serialization_version'] = $arguments['serializationVersion']; } if (isset($arguments['form'])) { $syliusOptions['form'] = $arguments['form']; } if (isset($arguments['section'])) { $syliusOptions['section'] = $arguments['section']; } if (isset($arguments['permission'])) { $syliusOptions['permission'] = $arguments['permission']; } if (isset($arguments['grid'])) { $syliusOptions['grid'] = $arguments['grid']; } if (isset($arguments['csrfProtection'])) { $syliusOptions['csrf_protection'] = $arguments['csrfProtection']; } if (isset($arguments['redirect'])) { $syliusOptions['redirect'] = $arguments['redirect']; } if (isset($arguments['stateMachine'])) { $syliusOptions['state_machine'] = $arguments['stateMachine']; } if (isset($arguments['event'])) { $syliusOptions['event'] = $arguments['event']; } if (isset($arguments['returnContent'])) { $syliusOptions['return_content'] = $arguments['returnContent']; } $route = new Route( $arguments['path'], [ '_controller' => $arguments['controller'] ?? null, '_sylius' => $syliusOptions, ], $arguments['requirements'] ?? [], $arguments['options'] ?? [], $arguments['host'] ?? '', $arguments['schemes'] ?? [], $arguments['methods'] ?? [], ); $routeCollection->add($arguments['name'], $route, $arguments['priority'] ?? 0); } } } ================================================ FILE: src/Bundle/Routing/RouteAttributesFactoryInterface.php ================================================ mapping['paths'] ?? []; foreach (ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories($paths) as $reflectionClass) { $className = $reflectionClass->getName(); $this->routesAttributesFactory->createRouteForClass($routeCollection, $className); $this->attributesOperationRouteFactory->createRouteForClass($routeCollection, $className); } return $routeCollection; } } ================================================ FILE: src/Bundle/Storage/CookieStorage.php ================================================ requestCookies = new ParameterBag(); $this->responseCookies = new ParameterBag(); } public static function getSubscribedEvents(): array { return [ KernelEvents::REQUEST => [['onKernelRequest', 1024]], KernelEvents::RESPONSE => [['onKernelResponse', -1024]], ]; } public function onKernelRequest(RequestEvent $event): void { if (!$event->isMainRequest()) { return; } $this->requestCookies = new ParameterBag($event->getRequest()->cookies->all()); $this->responseCookies = new ParameterBag(); } public function onKernelResponse(ResponseEvent $event): void { if (!$event->isMainRequest()) { return; } $response = $event->getResponse(); /** @var string|null $value */ foreach ($this->responseCookies as $name => $value) { Assert::nullOrString($value); $response->headers->setCookie(new Cookie($name, $value)); } $this->requestCookies = new ParameterBag(); $this->responseCookies = new ParameterBag(); } public function has(string $name): bool { return !in_array($this->get($name), ['', null], true); } public function get(string $name, $default = null) { return $this->responseCookies->get($name, $this->requestCookies->get($name, $default)); } public function set(string $name, $value): void { $this->responseCookies->set($name, $value); } public function remove(string $name): void { $this->set($name, null); } public function all(): array { return array_merge($this->responseCookies->all(), $this->requestCookies->all()); } } ================================================ FILE: src/Bundle/Storage/SessionStorage.php ================================================ requestStack = $requestStack; } public function has(string $name): bool { return $this->getSession()->has($name); } public function get(string $name, $default = null) { return $this->getSession()->get($name, $default); } public function set(string $name, $value): void { $this->getSession()->set($name, $value); } public function remove(string $name): void { $this->getSession()->remove($name); } public function all(): array { return $this->getSession()->all(); } /** * @throws StorageUnavailableException */ private function getSession(): SessionInterface { try { if ($this->requestStack instanceof SessionInterface) { return $this->requestStack; } /** @phpstan-ignore-next-line */ return $this->requestStack->getSession(); } catch (SessionNotFoundException $exception) { throw new StorageUnavailableException($exception->getMessage(), $exception); } } } ================================================ FILE: src/Bundle/SyliusResourceBundle.php ================================================ addCompilerPass(new CsrfTokenManagerPass()); $container->addCompilerPass(new DisableMetadataCachePass()); $container->addCompilerPass(new FallbackToKernelDefaultLocalePass()); $container->addCompilerPass(new DoctrineContainerRepositoryFactoryPass()); $container->addCompilerPass(new DoctrineTargetEntitiesResolverPass(new TargetEntitiesResolver()), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1); $container->addCompilerPass(new MetadataMutatorPass()); $container->addCompilerPass(new RegisterFormBuilderPass()); $container->addCompilerPass(new RegisterFqcnControllersPass()); $container->addCompilerPass(new RegisterResourceRepositoryPass()); $container->addCompilerPass(new RegisterResourcesPass()); $container->addCompilerPass(new RegisterStateMachinePass()); $container->addCompilerPass(new RegisterResourceStateMachinePass()); $container->addCompilerPass(new UnregisterFosRestDefinitionsPass()); $container->addCompilerPass(new UnregisterHateoasDefinitionsPass()); $container->addCompilerPass(new TwigPass()); $container->addCompilerPass(new WinzouStateMachinePass()); $container->registerExtension(new PagerfantaExtension(true)); $container->addCompilerPass(new PagerfantaBridgePass(true), PassConfig::TYPE_BEFORE_OPTIMIZATION, -1); // Should run after all passes from BabDevPagerfantaBundle } /** * @return string[] */ public static function getAvailableDrivers(): array { return [ self::DRIVER_DOCTRINE_ORM, self::DRIVER_DOCTRINE_MONGODB_ODM, self::DRIVER_DOCTRINE_PHPCR_ODM, ]; } } ================================================ FILE: src/Bundle/Twig/Context/LegacyContextFactory.php ================================================ decorated->create($data, $operation, $context); $requestConfiguration = $context->get(RequestConfigurationOption::class)?->requestConfiguration(); $metadata = $context->get(MetadataOption::class)?->metadata(); if (null !== $requestConfiguration) { $twigContext['configuration'] = $requestConfiguration; } if (null !== $metadata) { $twigContext['metadata'] = $metadata; } return $twigContext; } } ================================================ FILE: src/Bundle/Validator/Constraints/Disabled.php ================================================ isEnabled()) { $this->context->addViolation($constraint->message); } } } ================================================ FILE: src/Bundle/Validator/EnabledValidator.php ================================================ isEnabled()) { $this->context->addViolation($constraint->message); } } } ================================================ FILE: src/Bundle/Validator/UniqueWithinCollectionConstraintValidator.php ================================================ $entity) { /** @var int|string|null $checkingAttribute */ $checkingAttribute = $propertyAccessor->getValue($entity, $constraint->attributePath); if (null === $checkingAttribute) { continue; } if (!array_key_exists($checkingAttribute, $collectionOfEntitiesCodes)) { $collectionOfEntitiesCodes[$checkingAttribute] = $key; continue; } $this->context ->buildViolation($constraint->message) ->atPath(sprintf('[%d].%s', $key, $constraint->attributePath)) ->addViolation() ; if (false !== $collectionOfEntitiesCodes[$checkingAttribute]) { $this->context ->buildViolation($constraint->message) ->atPath(sprintf('[%d].%s', $collectionOfEntitiesCodes[$checkingAttribute], $constraint->attributePath)) ->addViolation() ; $collectionOfEntitiesCodes[$checkingAttribute] = false; } } } } ================================================ FILE: src/Component/.gitignore ================================================ /vendor/ !/vendor/.gitignore /bin/ /composer.phar /composer.lock /.phpunit.result.cache ================================================ FILE: src/Component/LICENSE ================================================ Copyright (c) 2011-2023 (c) Sylius Sp. z o.o. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: src/Component/README.md ================================================ Resource Component ================== Resource management layer for Sylius and PHP apps. Sylius ------ ![Sylius](https://demo.sylius.com/assets/shop/img/logo.png) Sylius is an Open Source eCommerce solution built from decoupled components with powerful API and the highest quality code. [Read more on sylius.com](http://sylius.com). Documentation ------------- Documentation is available on [**docs.sylius.com**](http://docs.sylius.com/en/latest/components_and_bundles/components/Resource/index.html). Contributing ------------ [This page](http://docs.sylius.com/en/latest/contributing/index.html) contains all the information about contributing to Sylius. Follow Sylius' Development -------------------------- If you want to keep up with the updates and latest features, follow us on the following channels: * [Official Blog](https://sylius.com/blog) * [Sylius on Twitter](https://twitter.com/Sylius) * [Sylius on Facebook](https://facebook.com/SyliusEcommerce) Bug tracking ------------ Sylius uses [GitHub issues](https://github.com/Sylius/Sylius/issues). If you have found bug, please create an issue. MIT License ----------- License can be found [here](https://github.com/Sylius/Sylius/blob/master/LICENSE). Authors ------- The component was originally created by [Paweł Jędrzejewski](http://pjedrzejewski.com). See the list of [contributors](https://github.com/Sylius/Resource/contributors). ================================================ FILE: src/Component/composer.json ================================================ { "name": "sylius/resource", "type": "library", "description": "Basic resource interfaces for PHP applications.", "keywords": [ "shop", "ecommerce", "resource", "api", "sylius", "doctrine" ], "homepage": "http://sylius.com", "license": "MIT", "authors": [ { "name": "Paweł Jędrzejewski", "homepage": "http://pjedrzejewski.com" }, { "name": "Sylius project", "homepage": "http://sylius.com" }, { "name": "Community contributions", "homepage": "http://github.com/Sylius/Sylius/contributors" } ], "require": { "php": "^8.2", "doctrine/collections": "^1.8 || ^2.0", "doctrine/inflector": "^1.4 || ^2.0", "gedmo/doctrine-extensions": "^2.4.12 || ^3.0", "pagerfanta/core": "^3.7 || ^4.0", "symfony/event-dispatcher": "^6.4 || ^7.4 || ^8.0", "symfony/form": "^6.4 || ^7.4 || ^8.0", "symfony/framework-bundle": "^6.4 || ^7.4 || ^8.0", "symfony/http-foundation": "^6.4 || ^7.4 || ^8.0", "symfony/http-kernel": "^6.4 || ^7.4 || ^8.0", "symfony/property-access": "^6.4 || ^7.4 || ^8.0", "symfony/routing": "^6.4 || ^7.4 || ^8.0", "symfony/security-core": "^6.4 || ^7.4 || ^8.0", "symfony/security-csrf": "^6.4 || ^7.4 || ^8.0", "symfony/string": "^6.4 || ^7.4 || ^8.0", "symfony/translation": "^6.4 || ^7.4 || ^8.0", "symfony/validator": "^6.4 || ^7.4 || ^8.0", "willdurand/negotiation": "^3.1" }, "require-dev": { "behat/transliterator": "^1.3", "doctrine/orm": "^2.18 || ^3.3", "matthiasnoback/symfony-dependency-injection-test": "^6.1.0", "phpunit/phpunit": "^10.0", "sylius/grid": "^1.13 || ^1.15@alpha", "sylius/grid-bundle": "^1.13 || ^1.15@alpha", "symfony/serializer": "^6.4 || ^7.4 || ^8.0", "symfony/workflow": "^6.4 || ^7.4 || ^8.0", "twig/twig": "^3.0" }, "conflict": { "doctrine/orm": "<2.18", "twig/twig": "<3.0", "winzou/state-machine": "<0.4" }, "extra": { "branch-alias": { "dev-master": "1.9-dev" } }, "autoload": { "psr-4": { "Sylius\\Component\\Resource\\": "legacy/src/", "Sylius\\Resource\\": "src/" } }, "autoload-dev": { "psr-4": { "Sylius\\Component\\Resource\\Tests\\": "legacy/tests/", "Sylius\\Resource\\Tests\\": "tests/" } } } ================================================ FILE: src/Component/legacy/phpspec.yml.dist ================================================ suites: main: namespace: Sylius\Component\Resource psr4_prefix: Sylius\Component\Resource src_path: legacy spec_path: legacy ================================================ FILE: src/Component/legacy/src/Annotation/SyliusCrudRoutes.php ================================================ ['dummy:write']])] #[Create] #[Update] #[Index] #[Show] final class DummyResourceWithDenormalizationContext { } ================================================ FILE: src/Component/legacy/tests/Dummy/DummyResourceWithFormOptions.php ================================================ false])] #[Update(formOptions: ['html5' => true])] final class DummyResourceWithFormOptions { } ================================================ FILE: src/Component/legacy/tests/Dummy/DummyResourceWithFormType.php ================================================ ['dummy:read']])] #[Create] #[Update] #[Index] #[Show] final class DummyResourceWithNormalizationContext { } ================================================ FILE: src/Component/legacy/tests/Dummy/DummyResourceWithOperations.php ================================================ 'en|fr|pl'])] #[Show(path: '/{page}', routeRequirements: ['_locale' => 'en|fr|pl', 'page' => '\d+'])] #[Create] #[Update] #[Index] final class DummyResourceWithRouteRequirements { } ================================================ FILE: src/Component/legacy/tests/Dummy/DummyResourceWithSections.php ================================================ ['sylius']])] #[Create] #[Update] final class DummyResourceWithValidationContext { } ================================================ FILE: src/Component/legacy/tests/Dummy/ProcessorWithCallable.php ================================================ id = $id; return $stdClass; } public static function findOneBy(array $criteria, ?array $orderBy = null): array { return []; } public function __call(string $method, mixed $arguments): array { return []; } } ================================================ FILE: src/Component/legacy/tests/Dummy/ResponderWithCallable.php ================================================ assertInstanceOf(NewDeleteHandlingException::class, new DeleteHandlingException()); } } ================================================ FILE: src/Component/legacy/tests/Exception/RaceConditionExceptionTest.php ================================================ assertInstanceOf(NewRaceConditionException::class, new RaceConditionException()); } } ================================================ FILE: src/Component/legacy/tests/Exception/UnexpectedTypeExceptionTest.php ================================================ assertInstanceOf(NewUnexpectedTypeException::class, new UnexpectedTypeException('stringValue', '\ExpectedType')); } } ================================================ FILE: src/Component/legacy/tests/Exception/UnsupportedMethodExceptionTest.php ================================================ assertInstanceOf(NewUnsupportedMethodException::class, new UnsupportedMethodException('methodName')); } } ================================================ FILE: src/Component/legacy/tests/Exception/UpdateHandlingExceptionTest.php ================================================ assertInstanceOf(NewUpdateHandlingException::class, new UpdateHandlingException()); } } ================================================ FILE: src/Component/legacy/tests/Factory/FactoryTest.php ================================================ factory = new Factory(\stdClass::class); } /** @test */ public function it_implements_factory_interface(): void { $this->assertInstanceOf(FactoryInterface::class, $this->factory); } /** @test */ public function it_implements_legacy_factory_interface(): void { $this->assertInstanceOf(LegacyFactoryInterface::class, $this->factory); } /** @test */ public function it_is_an_alias_of_the_factory(): void { $this->assertInstanceOf(NewFactory::class, $this->factory); } /** @test */ public function it_creates_a_new_instance_of_a_resource(): void { $this->assertInstanceOf(\stdClass::class, $this->factory->createNew()); } } ================================================ FILE: src/Component/legacy/tests/Factory/TranslatableFactoryTest.php ================================================ factory = $this->createMock(FactoryInterface::class); $this->localeProvider = $this->createMock(TranslationLocaleProviderInterface::class); $this->translatableFactory = new TranslatableFactory($this->factory, $this->localeProvider); } /** @test */ public function it_implements_translatable_factory_interface(): void { $this->assertInstanceOf(TranslatableFactoryInterface::class, $this->translatableFactory); } /** @test */ public function it_implements_legacy_translatable_factory_interface(): void { $this->assertInstanceOf(LegacyTranslatableFactoryInterface::class, $this->translatableFactory); } /** @test */ public function it_should_be_an_alias_of_translatable_factory(): void { $this->assertInstanceOf(NewTranslatableFactory::class, $this->translatableFactory); } } ================================================ FILE: src/Component/legacy/tests/Fixtures/SampleBookResourceInterface.php ================================================ legacyMetadata = ComponentMetadata::fromAliasAndConfiguration('sylius.product', ['driver' => 'doctrine/orm']); } /** @test */ public function it_implements_metadata_interface(): void { $this->assertInstanceOf(MetadataInterface::class, $this->legacyMetadata); } /** @test */ public function it_implements_legacy_metadata_interface(): void { $this->assertInstanceOf(LegacyMetadataInterface::class, $this->legacyMetadata); } /** @test */ public function it_should_be_an_alias_of_metadata(): void { $this->assertInstanceOf(Metadata::class, $this->legacyMetadata); } /** * @test * * @runInSeparateProcess * * @preserveGlobalState disabled */ public function it_uses_a_custom_inflector(): void { $factory = InflectorFactory::create(); $factory->withPluralRules(new Ruleset( new Transformations(), new Patterns(), new Substitutions(new Substitution(new Word('taxon'), new Word('taxons'))), )); $inflector = $factory->build(); Metadata::setInflector($inflector); $metadata = Metadata::fromAliasAndConfiguration('app.taxon', ['driver' => 'whatever']); Assert::assertSame('taxons', $metadata->getPluralName()); } /** @test */ public function it_uses_a_default_inflector(): void { $metadata = Metadata::fromAliasAndConfiguration('app.taxon', ['driver' => 'whatever']); Assert::assertSame('taxa', $metadata->getPluralName()); } } ================================================ FILE: src/Component/legacy/tests/Metadata/RegistryTest.php ================================================ registry = new Registry(); } public function testItImplementsRegistryInterface(): void { $this->assertInstanceOf(RegistryInterface::class, $this->registry); } public function testItImplementsLegacyRegistryInterface(): void { $this->assertInstanceOf(LegacyRegistryInterface::class, $this->registry); } public function testItShouldBeAnAliasOfRegistry(): void { $this->assertInstanceOf(NewRegistry::class, $this->registry); } } ================================================ FILE: src/Component/legacy/tests/Model/AbstractTranslationTest.php ================================================ translation = new ConcreteTranslation(); } public function testItIsATranslation(): void { $this->assertInstanceOf(TranslationInterface::class, $this->translation); } public function testItShouldBeAnAliasOfAbstractTranslation(): void { $this->assertInstanceOf(NewAbstractTranslation::class, $this->translation); } } class ConcreteTranslation extends AbstractTranslation { } ================================================ FILE: src/Component/legacy/tests/Reflection/ClassInfoTraitTest.php ================================================ getClassInfoTraitImplementation(); $this->assertSame(Book::class, $classInfo->getRealClassName($class)); } /** * @dataProvider getInvalidClasses */ public function testThrowsExceptionIfUnproxiedClassDoNotExist(string $class): void { $this->expectException(\InvalidArgumentException::class); $classInfo = $this->getClassInfoTraitImplementation(); $classInfo->getRealClassName($class); } public function getInvalidClasses(): Iterable { yield ['class' => 'Proxies\__CG__\App\Entity\Book1']; yield ['class' => 'MongoDBODMProxies\__PM__\App\Entity\Book1\Generated']; } public function getValidClasses(): Iterable { yield ['class' => 'Proxies\__CG__\App\Entity\Book']; yield ['class' => 'MongoDBODMProxies\__PM__\App\Entity\Book\Generated']; } public function testUnmarkedRealClassName(): void { $classInfo = $this->getClassInfoTraitImplementation(); $this->assertSame(Book::class, $classInfo->getRealClassName(Book::class)); } } ================================================ FILE: src/Component/legacy/tests/Reflection/ClassReflectionTest.php ================================================ assertInstanceOf(NewClassReflection::class, new \Sylius\Component\Resource\Reflection\ClassReflection()); } /** @test */ public function it_returns_resource_classes_from_paths(): void { $resources = ClassReflection::getResourcesByPaths([__DIR__ . '/../Dummy']); $this->assertContains(DummyClassOne::class, $resources); $this->assertContains(DummyClassTwo::class, $resources); } /** @test */ public function it_returns_resource_classes_from_a_directory(): void { $resources = ClassReflection::getResourcesByPath(__DIR__ . '/../Dummy'); $this->assertContains(DummyClassOne::class, $resources); $this->assertContains(DummyClassTwo::class, $resources); } /** @test */ public function it_excludes_traits(): void { $resources = ClassReflection::getResourcesByPath(__DIR__ . '/../Dummy'); $this->assertNotContains(TraitPass::class, $resources); } /** @test */ public function it_returns_class_attributes(): void { $this->assertCount(1, ClassReflection::getClassAttributes(BookWithCriteria::class, SyliusCrudRoutes::class)); $this->assertCount(1, ClassReflection::getClassAttributes(ShowBook::class, SyliusRoute::class)); $this->assertCount(2, ClassReflection::getClassAttributes(ShowBookWithPriority::class, SyliusRoute::class)); } } ================================================ FILE: src/Component/legacy/tests/Repository/Exception/ExistingResourceExceptionTest.php ================================================ exception = new ExistingResourceException(); } public function testItExtendsException(): void { $this->assertInstanceOf(\Exception::class, $this->exception); } public function testItShouldBeAnAliasOfResourceExistsException(): void { $this->assertInstanceOf(ResourceExistsException::class, $this->exception); } } ================================================ FILE: src/Component/legacy/tests/Repository/InMemoryRepositoryTest.php ================================================ repository = new InMemoryRepository(SampleBookResourceInterface::class); } public function testItShouldBeAnAliasOfInMemoryRepository(): void { $this->assertInstanceOf(NewInMemoryRepository::class, $this->repository); } } ================================================ FILE: src/Component/legacy/tests/ResourceActionsTest.php ================================================ assertTrue(class_exists(ResourceActions::class)); } public function testItIsAnAliasOfResourceActions(): void { $this->assertTrue(is_a(ResourceActions::class, NewResourceActions::class, true)); } } ================================================ FILE: src/Component/legacy/tests/StateMachine/StateMachineTest.php ================================================ markAsSkippedIfWinzouStateMachineIsNotAvailable(); $this->stateMachine = new StateMachine(new PullRequest(), [ 'graph' => 'pull_request', 'property_path' => 'currentPlace', 'places' => [ 'start', 'test', ], 'transitions' => [ 'submit' => [ 'from' => ['start'], 'to' => 'test', ], ], ]); } public function testItIsInitializable(): void { $this->assertInstanceOf(StateMachine::class, $this->stateMachine); } public function testItImplementsStateMachineInterface(): void { $this->assertInstanceOf(StateMachineInterface::class, $this->stateMachine); } public function testItImplementsLegacyStateMachineInterface(): void { $this->assertInstanceOf(LegacyStateMachineInterface::class, $this->stateMachine); } public function testItShouldBeAnAliasOfStateMachine(): void { $this->assertInstanceOf(NewStateMachine::class, $this->stateMachine); } private function markAsSkippedIfWinzouStateMachineIsNotAvailable(): void { if (!class_exists(winzouStateMachineBundle::class)) { $this->markTestSkipped('Winzou State machine is not available.'); } } } ================================================ FILE: src/Component/legacy/tests/Symfony/EventListener/AddFormatListenerTest.php ================================================ operationInitiator = $this->createMock(HttpOperationInitiatorInterface::class); $this->addFormatListener = new AddFormatListener( $this->operationInitiator, new Negotiator(), ); } /** @test */ public function it_sets_format_from_accept_header(): void { $event = $this->createMock(RequestEvent::class); $request = $this->createMock(Request::class); $operation = $this->createMock(HttpOperation::class); $event->method('getRequest')->willReturn($request); $this->operationInitiator->method('initializeOperation')->with($request)->willReturn($operation); $request->attributes = new ParameterBag(); $request->headers = new HeaderBag(['Accept' => 'application/json']); $request->expects($this->once()) ->method('getFormat') ->with('application/json') ->willReturn('json'); $request->expects($this->once()) ->method('setRequestFormat') ->with('json'); $this->addFormatListener->onKernelRequest($event); } /** @test */ public function it_sets_format_from_request(): void { $event = $this->createMock(RequestEvent::class); $request = $this->createMock(Request::class); $operation = $this->createMock(HttpOperation::class); $event->method('getRequest')->willReturn($request); $this->operationInitiator->method('initializeOperation')->with($request)->willReturn($operation); $request->attributes = new ParameterBag(['_format' => 'json']); $request->headers = new HeaderBag(); $request->expects($this->once()) ->method('getRequestFormat') ->with(null) ->willReturn('json'); $request->expects($this->once()) ->method('getMimeType') ->with('json') ->willReturn('application/json'); $this->addFormatListener->onKernelRequest($event); } /** @test */ public function it_throws_an_exception_when_format_is_not_accepted(): void { $event = $this->createMock(RequestEvent::class); $request = $this->createMock(Request::class); $operation = $this->createMock(HttpOperation::class); $event->method('getRequest')->willReturn($request); $this->operationInitiator->method('initializeOperation')->with($request)->willReturn($operation); $request->attributes = new ParameterBag(['_format' => 'json-ld']); $request->headers = new HeaderBag(); $request->expects($this->once()) ->method('getRequestFormat') ->with(null) ->willReturn('json-ld'); $request->expects($this->once()) ->method('getMimeType') ->with('json-ld') ->willReturn('application/json-ld'); $this->expectException(NotAcceptableHttpException::class); $this->expectExceptionMessage('Requested format "application/json-ld" is not supported. Supported MIME types are "text/html", "application/json", "application/xml".'); $this->addFormatListener->onKernelRequest($event); } } ================================================ FILE: src/Component/legacy/tests/Symfony/Validator/State/ValidateProviderTest.php ================================================ decorated = $this->createMock(ProviderInterface::class); $this->validator = $this->createMock(ValidatorInterface::class); $this->validateProvider = new ValidateProvider( $this->decorated, $this->validator, ); } /** @test */ public function it_validates_form_data(): void { $request = $this->createMock(Request::class); $form = $this->createMock(FormInterface::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $operation = new Create(); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn(['foo' => 'fighters']) ; $request->method('isMethodSafe')->willReturn(false); $request->method('getRequestFormat')->willReturn('html'); $request->attributes = $attributes; $attributes->method('get')->with('form')->willReturn($form); $form->expects($this->once())->method('isSubmitted')->willReturn(true); $form->expects($this->once())->method('isValid')->willReturn(true); $form->expects($this->once())->method('getData')->willReturn($data); $attributes->expects($this->once()) ->method('set') ->with('is_valid', true) ; $this->validateProvider->provide($operation, $context); } /** @test */ public function it_does_nothing_if_controller_result_is_a_response(): void { $request = $this->createMock(Request::class); $form = $this->createMock(FormInterface::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $operation = new Create(); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn(new Response()) ; $request->expects($this->once())->method('getRequestFormat')->willReturn('html'); $request->expects($this->never())->method('isMethodSafe'); $request->attributes = $attributes; $attributes->method('get')->with('form')->willReturn($form); $form->expects($this->never())->method('isSubmitted'); $form->expects($this->never())->method('isValid'); $form->expects($this->never())->method('getData'); $attributes->expects($this->never())->method('set')->with('is_valid', true); $this->validateProvider->provide($operation, $context); } /** @test */ public function it_does_nothing_if_operation_is_not_a_create_or_update_operation(): void { $request = $this->createMock(Request::class); $form = $this->createMock(FormInterface::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $operation = new Index(); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn(['foo' => 'fighters']) ; $request->method('isMethodSafe')->willReturn(false); $request->method('getRequestFormat')->willReturn('html'); $request->attributes = $attributes; $attributes->method('get')->willReturnCallback(function (string $key) use ($form) { if ($key === 'form') { return $form; } if ($key === '_route') { return 'app_dummy_create'; } return null; }); $attributes->method('all')->with('_sylius')->willReturn(['resource' => 'app.dummy']); $form->expects($this->never())->method('isSubmitted'); $form->expects($this->never())->method('isValid'); $form->expects($this->never())->method('getData'); $attributes->expects($this->never())->method('set')->with('is_valid', true); $this->validateProvider->provide($operation, $context); } /** @test */ public function it_sets_is_valid_to_false_if_method_is_safe(): void { $request = $this->createMock(Request::class); $form = $this->createMock(FormInterface::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $operation = new Create(); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn(['foo' => 'fighters']) ; $request->expects($this->once())->method('isMethodSafe')->willReturn(true); $request->expects($this->once())->method('getRequestFormat')->willReturn('html'); $request->attributes = $attributes; $attributes->method('get')->with('form')->willReturn($form); $form->expects($this->never())->method('isSubmitted'); $form->expects($this->never())->method('isValid'); $form->expects($this->never())->method('getData'); $attributes->expects($this->once()) ->method('set') ->with('is_valid', false) ; $this->validateProvider->provide($operation, $context); } /** @test */ public function it_does_nothing_if_there_is_no_form(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $operation = new Create(); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn(['foo' => 'fighters']) ; $request->method('isMethodSafe')->willReturn(false); $request->method('getRequestFormat')->willReturn('html'); $request->attributes = $attributes; $attributes->method('get')->with('form')->willReturn(null); $attributes->expects($this->never())->method('set')->with('is_valid', $this->anything()); $this->validateProvider->provide($operation, $context); } /** @test */ public function it_validates_resource_on_non_html_format(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $constraintViolationList = $this->createMock(ConstraintViolationListInterface::class); $request->expects($this->once())->method('getRequestFormat')->willReturn('json'); $operation = new Create(); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data) ; $request->attributes = $attributes; $attributes->method('get')->with('form')->willReturn(null); $this->validator->expects($this->once()) ->method('validate') ->with($data, null, null) ->willReturn($constraintViolationList) ; $constraintViolationList->expects($this->once())->method('count')->willReturn(0); $this->validateProvider->provide($operation, $context); } /** @test */ public function it_validates_resource_with_validation_context_on_non_html_format(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $constraintViolationList = $this->createMock(ConstraintViolationListInterface::class); $request->expects($this->once())->method('getRequestFormat')->willReturn('json'); $operation = new Create(validationContext: ['groups' => ['sylius']]); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data) ; $request->attributes = $attributes; $attributes->method('get')->with('form')->willReturn(null); $this->validator->expects($this->once()) ->method('validate') ->with($data, null, ['sylius']) ->willReturn($constraintViolationList) ; $constraintViolationList->expects($this->once())->method('count')->willReturn(0); $this->validateProvider->provide($operation, $context); } /** @test */ public function it_throws_an_exception_when_validating_resource_on_non_html_format(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $constraintViolation = $this->createMock(ConstraintViolationInterface::class); $operation = new Create(); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data) ; $request->expects($this->once())->method('getRequestFormat')->willReturn('json'); $request->attributes = $attributes; $attributes->expects($this->once())->method('get')->with('form')->willReturn(null); $constraintViolation->method('getPropertyPath')->willReturn('property'); $constraintViolation->method('getMessage')->willReturn('Error message'); $constraintViolationList = new ConstraintViolationList([$constraintViolation]); $this->validator->expects($this->once()) ->method('validate') ->with($data, null, null) ->willReturn($constraintViolationList) ; $this->expectException(ValidationException::class); $this->validateProvider->provide($operation, $context); } /** @test */ public function it_does_nothing_if_operation_cannot_be_validated(): void { $request = $this->createMock(Request::class); $form = $this->createMock(FormInterface::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $operation = new Create(validate: false); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data) ; $request->expects($this->once())->method('getRequestFormat')->willReturn('html'); $request->expects($this->never())->method('isMethodSafe'); $request->attributes = $attributes; $attributes->expects($this->once())->method('get')->with('form')->willReturn($form); $form->expects($this->never())->method('isSubmitted'); $form->expects($this->never())->method('isValid'); $form->expects($this->never())->method('getData'); $attributes->expects($this->never())->method('set')->with('is_valid', true); $result = $this->validateProvider->provide($operation, $context); Assert::eq($result, $data); } } ================================================ FILE: src/Component/legacy/tests/Translation/Provider/ImmutableTranslationLocaleProviderTest.php ================================================ provider = new ImmutableTranslationLocaleProvider(['pl_PL', 'en_US'], 'pl_PL'); } public function testItImplementsTranslationLocaleProviderInterface(): void { $this->assertInstanceOf(TranslationLocaleProviderInterface::class, $this->provider); } public function testItImplementsLegacyTranslationLocaleProviderInterface(): void { $this->assertInstanceOf(LegacyTranslationLocaleProviderInterface::class, $this->provider); } public function testItIsAnAliasOfImmutableTranslationLocaleProvider(): void { $this->assertInstanceOf(NewImmutableTranslationLocaleProvider::class, $this->provider); } } ================================================ FILE: src/Component/legacy/tests/Translation/TranslatableEntityLocaleAssignerTest.php ================================================ createMock(TranslationLocaleProviderInterface::class); $this->assigner = new TranslatableEntityLocaleAssigner($translationLocaleProvider); } public function testItImplementsTranslatableEntityLocaleAssignerInterface(): void { $this->assertInstanceOf(TranslatableEntityLocaleAssignerInterface::class, $this->assigner); } public function testItImplementsLegacyTranslatableEntityLocaleAssignerInterface(): void { $this->assertInstanceOf(LegacyTranslatableEntityLocaleAssignerInterface::class, $this->assigner); } public function testItIsAnAliasOfTranslatableEntityLocalAssigner(): void { $this->assertInstanceOf(NewTranslatableEntityLocaleAssigner::class, $this->assigner); } } ================================================ FILE: src/Component/phpunit.xml.dist ================================================ ./legacy/tests/ ================================================ FILE: src/Component/src/Annotation/SyliusCrudRoutes.php ================================================ alias = $alias; $this->path = $path; $this->identifier = $identifier; $this->criteria = $criteria; $this->filterable = $filterable; $this->form = $form; $this->serializationVersion = $serializationVersion; $this->section = $section; $this->redirect = $redirect; $this->templates = $templates; $this->grid = $grid; $this->permission = $permission; $this->except = $except; $this->only = $only; $this->vars = $vars; } } if (!class_exists(\Sylius\Component\Resource\Annotation\SyliusCrudRoutes::class, false)) { class_alias(SyliusCrudRoutes::class, \Sylius\Component\Resource\Annotation\SyliusCrudRoutes::class); } ================================================ FILE: src/Component/src/Annotation/SyliusRoute.php ================================================ */ final class Context implements \IteratorAggregate { /** @var array */ private array $optionMap; public function __construct(object ...$options) { $map = []; foreach ($options as $option) { $map[get_class($option)] = $option; } $this->optionMap = $map; } public function with(object ...$options): self { /** @psalm-suppress DuplicateArrayKey */ return new self(...[ ...array_values($this->optionMap), ...$options, ]); } /** * @param class-string $optionClasses */ public function without(string ...$optionClasses): self { $optionMap = $this->optionMap; foreach ($optionClasses as $optionClass) { unset($optionMap[$optionClass]); } return new self(...array_values($optionMap)); } /** * @template T of object * * @param class-string $optionClass * * @return T|null */ public function get(string $optionClass): ?object { /** @var T $option */ $option = $this->optionMap[$optionClass] ?? null; return $option; } /** * @return \Traversable */ public function getIterator(): \Traversable { yield from array_values($this->optionMap); } } ================================================ FILE: src/Component/src/Context/Initiator/RequestContextInitiator.php ================================================ metadata; } } ================================================ FILE: src/Component/src/Context/Option/RequestOption.php ================================================ request; } } ================================================ FILE: src/Component/src/Context/Option/ResourceClassOption.php ================================================ resourceClass; } } ================================================ FILE: src/Component/src/Doctrine/Common/Metadata/Resource/Factory/DoctrineResourceMetadataCollectionFactory.php ================================================ decorated->create($resourceClass); /** @var ResourceMetadata $resource */ foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) { $operations = $resource->getOperations() ?? new Operations(); /** @var Operation $operation */ foreach ($operations as $operation) { /** @var string $key */ $key = $operation->getName(); $operations->add($key, $this->addDefaults($resource, $operation)); } $resource = $resource->withOperations($operations); $resourceCollectionMetadata[$i] = $resource; } return $resourceCollectionMetadata; } private function addDefaults(ResourceMetadata $resource, Operation $operation): Operation { $metadata = $this->resourceRegistry->get($resource->getAlias() ?? ''); $driver = $metadata->getDriver(); if ($driver && str_starts_with($driver, 'doctrine/')) { $operation = $operation->withProcessor($this->getProcessor($operation)); } return $operation; } private function getProcessor(Operation $operation): callable|string { if (null !== $processor = $operation->getProcessor()) { return $processor; } if ($operation instanceof DeleteOperationInterface) { return RemoveProcessor::class; } return PersistProcessor::class; } } ================================================ FILE: src/Component/src/Doctrine/Common/State/PersistProcessor.php ================================================ getManager($data)) { return $data; } if (!$manager->contains($data) || $this->isDeferredExplicit($manager, $data)) { $manager->persist($data); } $manager->flush(); $manager->refresh($data); return $data; } /** * Gets the Doctrine object manager associated with given data. */ private function getManager(object $data): ?DoctrineObjectManager { return $this->managerRegistry->getManagerForClass($this->getObjectClass($data)); } /** * Checks if doctrine does not manage data automatically. */ private function isDeferredExplicit(DoctrineObjectManager $manager, object $data): bool { $classMetadata = $manager->getClassMetadata($this->getObjectClass($data)); if ($classMetadata instanceof ClassMetadata && method_exists($classMetadata, 'isChangeTrackingDeferredExplicit')) { return $classMetadata->isChangeTrackingDeferredExplicit(); } return false; } } ================================================ FILE: src/Component/src/Doctrine/Common/State/RemoveProcessor.php ================================================ getManager($data)) { return null; } try { $manager->remove($data); $manager->flush(); } catch (ForeignKeyConstraintViolationException) { throw new DeleteResourceException(); } return $data; } /** * Gets the Doctrine object manager associated with given data. */ private function getManager(object $data): ?DoctrineObjectManager { return $this->managerRegistry->getManagerForClass($this->getObjectClass($data)); } } ================================================ FILE: src/Component/src/Doctrine/Persistence/Exception/ExceptionInterface.php ================================================ interface = $interface; $this->accessor = PropertyAccess::createPropertyAccessor(); $this->arrayObject = new \ArrayObject(); } /** * @throws ResourceExistsException * @throws UnexpectedTypeException */ public function add(ResourceInterface $resource): void { if (!$resource instanceof $this->interface) { throw new UnexpectedTypeException($resource, $this->interface); } if (in_array($resource, $this->findAll(), true)) { throw new ResourceExistsException(); } $this->arrayObject->append($resource); } public function remove(ResourceInterface $resource): void { $newResources = array_filter($this->findAll(), static function ($object) use ($resource) { return $object !== $resource; }); $this->arrayObject->exchangeArray($newResources); } public function find($id): ?object { return $this->findOneBy(['id' => $id]); } public function findAll(): array { $arrayCopy = $this->arrayObject->getArrayCopy(); Assert::allObject($arrayCopy); return $arrayCopy; } public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array { $results = $this->findAll(); if (!empty($criteria)) { $results = $this->applyCriteria($results, $criteria); } if (!empty($orderBy)) { $results = $this->applyOrder($results, $orderBy); } return array_slice($results, $offset ?? 0, $limit); } /** * @throws \InvalidArgumentException */ public function findOneBy(array $criteria): ?ResourceInterface { if (empty($criteria)) { throw new \InvalidArgumentException('The criteria array needs to be set.'); } $results = $this->applyCriteria($this->findAll(), $criteria); /** @var ResourceInterface|false $result */ $result = reset($results); if ($result !== false) { return $result; } return null; } public function getClassName(): string { return $this->interface; } /** * @return PagerfantaInterface */ public function createPaginator(array $criteria = [], array $sorting = []): iterable { $resources = $this->findAll(); if (!empty($sorting)) { $resources = $this->applyOrder($resources, $sorting); } if (!empty($criteria)) { $resources = $this->applyCriteria($resources, $criteria); } return new Pagerfanta(new ArrayAdapter($resources)); } /** * @param object[] $resources * * @return object[]|array */ private function applyCriteria(array $resources, array $criteria): array { /** @var array|object $object */ foreach ($this->arrayObject as $object) { foreach ($criteria as $criterion => $value) { if ($value !== $this->accessor->getValue($object, $criterion)) { $key = array_search($object, $resources); unset($resources[$key]); } } } return $resources; } /** * @param object[] $resources * * @return object[] */ private function applyOrder(array $resources, array $orderBy): array { $results = $resources; $arguments = []; foreach ($orderBy as $property => $order) { $sortable = []; foreach ($results as $key => $object) { $sortable[$key] = $this->accessor->getValue($object, $property); } $arguments[] = $sortable; if (RepositoryInterface::ORDER_ASCENDING === $order) { $arguments[] = \SORT_ASC; } elseif (RepositoryInterface::ORDER_DESCENDING === $order) { $arguments[] = \SORT_DESC; } else { throw new \InvalidArgumentException('Unknown order.'); } } $arguments[] = &$results; /** * Doing PHP magic, it works this way * * @psalm-suppress InvalidPassByReference * @psalm-suppress PossiblyInvalidArgument */ array_multisort(...$arguments); return $results; } } if (!class_exists(\Sylius\Component\Resource\Repository\InMemoryRepository::class, false)) { class_alias(InMemoryRepository::class, \Sylius\Component\Resource\Repository\InMemoryRepository::class); } ================================================ FILE: src/Component/src/Doctrine/Persistence/RepositoryInterface.php ================================================ */ interface RepositoryInterface extends ObjectRepository { public const ORDER_ASCENDING = 'ASC'; public const ORDER_DESCENDING = 'DESC'; /** * @param array $criteria * @param array $sorting * * @return iterable */ public function createPaginator(array $criteria = [], array $sorting = []): iterable; public function add(ResourceInterface $resource): void; public function remove(ResourceInterface $resource): void; } if (!class_exists(\Sylius\Component\Resource\Repository\RepositoryInterface::class, false)) { class_alias(RepositoryInterface::class, \Sylius\Component\Resource\Repository\RepositoryInterface::class); } ================================================ FILE: src/Component/src/Exception/DeleteHandlingException.php ================================================ flash = $flash; $this->apiResponseCode = $apiResponseCode; } public function getFlash(): string { return $this->flash; } public function getApiResponseCode(): int { return $this->apiResponseCode; } } if (!class_exists(\Sylius\Component\Resource\Exception\DeleteHandlingException::class, false)) { class_alias(DeleteHandlingException::class, \Sylius\Component\Resource\Exception\DeleteHandlingException::class); } ================================================ FILE: src/Component/src/Exception/DeleteResourceException.php ================================================ getCode() : 0, $previous, ); } } if (!class_exists(\Sylius\Component\Resource\Exception\RaceConditionException::class, false)) { class_alias(RaceConditionException::class, \Sylius\Component\Resource\Exception\RaceConditionException::class); } ================================================ FILE: src/Component/src/Exception/RuntimeException.php ================================================ flash = $flash; $this->apiResponseCode = $apiResponseCode; } public function getFlash(): string { return $this->flash; } public function getApiResponseCode(): int { return $this->apiResponseCode; } } if (!class_exists(\Sylius\Component\Resource\Exception\UpdateHandlingException::class, false)) { class_alias(UpdateHandlingException::class, \Sylius\Component\Resource\Exception\UpdateHandlingException::class); } ================================================ FILE: src/Component/src/Exception/VariantWithNoOptionsValuesException.php ================================================ resourceName; } } ================================================ FILE: src/Component/src/Factory/Factory.php ================================================ className = $className; } public function createNew() { return new $this->className(); } } if (!class_exists(\Sylius\Component\Resource\Factory\Factory::class, false)) { class_alias(Factory::class, \Sylius\Component\Resource\Factory\Factory::class); } ================================================ FILE: src/Component/src/Factory/FactoryInterface.php ================================================ factory = $factory; $this->localeProvider = $localeProvider; } /** * @throws UnexpectedTypeException */ public function createNew() { $resource = $this->factory->createNew(); if (!$resource instanceof TranslatableInterface) { throw new UnexpectedTypeException($resource, TranslatableInterface::class); } $resource->setCurrentLocale($this->localeProvider->getDefaultLocaleCode()); $resource->setFallbackLocale($this->localeProvider->getDefaultLocaleCode()); return $resource; } } if (!class_exists(\Sylius\Component\Resource\Factory\TranslatableFactory::class, false)) { class_alias(TranslatableFactory::class, \Sylius\Component\Resource\Factory\TranslatableFactory::class); } ================================================ FILE: src/Component/src/Factory/TranslatableFactoryInterface.php ================================================ digits = implode(range(0, 9)); $this->uriSafeAlphabet = implode(range(0, 9)) . implode(range('a', 'z')) . implode(range('A', 'Z')) . implode(['-', '_', '~']) ; } public function generateUriSafeString(int $length): string { return $this->generateStringOfLength($length, $this->uriSafeAlphabet); } public function generateNumeric(int $length): string { return $this->generateStringOfLength($length, $this->digits); } public function generateInt(int $min, int $max): int { return random_int($min, $max); } private function generateStringOfLength(int $length, string $alphabet): string { $alphabetMaxIndex = strlen($alphabet) - 1; Assert::greaterThanEq($alphabetMaxIndex, 1); $randomString = ''; for ($i = 0; $i < $length; ++$i) { $index = random_int(0, $alphabetMaxIndex); $randomString .= $alphabet[$index]; } return $randomString; } } if (!class_exists(\Sylius\Component\Resource\Generator\RandomnessGenerator::class, false)) { class_alias(RandomnessGenerator::class, \Sylius\Component\Resource\Generator\RandomnessGenerator::class); } ================================================ FILE: src/Component/src/Generator/RandomnessGeneratorInterface.php ================================================ gridViewFactory || null === $this->gridProvider) { throw new \LogicException('You can not use a grid if Sylius Grid Bundle is not available. Try running "composer require sylius/grid-bundle".'); } if (!$operation instanceof GridAwareOperationInterface) { throw new \LogicException(sprintf('You can not use a grid if your operation does not implement "%s".', GridAwareOperationInterface::class)); } $grid = $operation->getGrid(); if (null === $grid) { throw new \RuntimeException(sprintf('Operation has no grid, so you cannot use this provider for operation "%s"', $operation->getName() ?? '')); } $request = $context->get(RequestOption::class)?->request(); if (null === $request) { return null; } $gridDefinition = $this->gridProvider->get($grid); $gridConfiguration = $gridDefinition->getDriverConfiguration(); $parameters = $request->query->all(); $gridView = $this->gridViewFactory->create($gridDefinition, $context, new Parameters($parameters), $gridConfiguration); $data = $gridView->getData(); if ($data instanceof Pagerfanta) { $currentPage = $request->query->getInt('page', 1); $data->setCurrentPage($currentPage); $maxPerPage = $this->resolveMaxPerPage( $request->query->has('limit') ? $request->query->getInt('limit') : null, $gridDefinition->getLimits(), ); $data->setMaxPerPage($maxPerPage); } return $gridView; } private function resolveMaxPerPage(?int $requestLimit, array $gridLimits = []): int { if (null === $requestLimit) { $firstGridLimit = reset($gridLimits); return false === $firstGridLimit ? self::DEFAULT_MAX_PER_PAGE : $firstGridLimit; } if (!empty($gridLimits)) { $maxGridLimit = max($gridLimits); // Cannot retrieve more items than configured in the grid return min($requestLimit, $maxGridLimit); } return $requestLimit; } } ================================================ FILE: src/Component/src/Grid/View/Factory/GridViewFactory.php ================================================ dataProvider->getData($grid, $parameters), $grid, $parameters); } } ================================================ FILE: src/Component/src/Grid/View/Factory/GridViewFactoryInterface.php ================================================ stateMachineComponent; } public function withStateMachineComponent(?string $stateMachineComponent): self { $self = clone $this; $self->stateMachineComponent = $stateMachineComponent; return $self; } public function getStateMachineTransition(): ?string { return $this->stateMachineTransition; } public function withStateMachineTransition(string $stateMachineTransition): self { $self = clone $this; $self->stateMachineTransition = $stateMachineTransition; return $self; } public function getStateMachineGraph(): ?string { return $this->stateMachineGraph; } public function withStateMachineGraph(string $stateMachineGraph): self { $self = clone $this; $self->stateMachineGraph = $stateMachineGraph; return $self; } } ================================================ FILE: src/Component/src/Metadata/AsOperationMutator.php ================================================ |null $routeRequirements */ public function __construct( private ?string $alias = null, private ?string $section = null, private ?string $formType = null, private ?string $templatesDir = null, private ?string $routePrefix = null, private ?array $routeRequirements = null, private ?string $routeCondition = null, private ?int $routePriority = null, private ?string $name = null, private ?string $pluralName = null, private ?string $applicationName = null, private ?string $identifier = null, private ?array $normalizationContext = null, private ?array $denormalizationContext = null, private ?array $validationContext = null, private ?string $class = null, private string|false|null $driver = null, private ?array $vars = null, private ?array $operations = null, ) { } public function toMetadata(): ResourceMetadata { return new ResourceMetadata( alias: $this->alias, section: $this->section, formType: $this->formType, templatesDir: $this->templatesDir, routePrefix: $this->routePrefix, routeRequirements: $this->routeRequirements, routeCondition: $this->routeCondition, routePriority: $this->routePriority, name: $this->name, pluralName: $this->pluralName, applicationName: $this->applicationName, identifier: $this->identifier, normalizationContext: $this->normalizationContext, denormalizationContext: $this->denormalizationContext, validationContext: $this->validationContext, class: $this->class, driver: $this->driver, vars: $this->vars, operations: $this->operations, ); } } ================================================ FILE: src/Component/src/Metadata/AsResourceMutator.php ================================================ stateMachineComponent; } public function withStateMachineComponent(?string $stateMachineComponent): self { $self = clone $this; $self->stateMachineComponent = $stateMachineComponent; return $self; } public function getStateMachineTransition(): ?string { return $this->stateMachineTransition; } public function withStateMachineTransition(string $stateMachineTransition): self { $self = clone $this; $self->stateMachineTransition = $stateMachineTransition; return $self; } public function getStateMachineGraph(): ?string { return $this->stateMachineGraph; } public function withStateMachineGraph(string $stateMachineGraph): self { $self = clone $this; $self->stateMachineGraph = $stateMachineGraph; return $self; } } ================================================ FILE: src/Component/src/Metadata/CollectionOperationInterface.php ================================================ factory = $factory; } public function getStateMachineComponent(): ?string { return $this->stateMachineComponent; } public function withStateMachineComponent(?string $stateMachineComponent): self { $self = clone $this; $self->stateMachineComponent = $stateMachineComponent; return $self; } public function getStateMachineTransition(): ?string { return $this->stateMachineTransition; } public function withStateMachineTransition(string $stateMachineTransition): self { $self = clone $this; $self->stateMachineTransition = $stateMachineTransition; return $self; } public function getStateMachineGraph(): ?string { return $this->stateMachineGraph; } public function withStateMachineGraph(string $stateMachineGraph): self { $self = clone $this; $self->stateMachineGraph = $stateMachineGraph; return $self; } public function getFactory(): callable|string|false|null { return $this->factory; } public function withFactory(string|callable|false|null $factory): self { $self = clone $this; $self->factory = $factory; return $self; } public function getFactoryMethod(): ?string { return $this->factoryMethod; } public function withFactoryMethod(string $factoryMethod): self { $self = clone $this; $self->factoryMethod = $factoryMethod; return $self; } public function getFactoryArguments(): ?array { return $this->factoryArguments; } public function withFactoryArguments(array $factoryArguments): self { $self = clone $this; $self->factoryArguments = $factoryArguments; return $self; } } ================================================ FILE: src/Component/src/Metadata/CreateOperationInterface.php ================================================ resources) { return $this->resources; } $this->resources = []; foreach ($this->paths as $path) { $this->extractFromPath($path); } return $this->resources; } /** * Extracts metadata from a given path. */ abstract protected function extractFromPath(string $path): void; /** * Recursively replaces placeholders with the service container parameters. * * @see https://github.com/symfony/symfony/blob/6fec32c/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php * * @param mixed $value The source which might contain "%placeholders%" * * @throws \RuntimeException When a container value is not a string or a numeric value * * @return mixed The source with the placeholders replaced by the container * parameters. Arrays are resolved recursively. */ protected function resolve(mixed $value): mixed { if (null === $this->container) { return $value; } if (\is_array($value)) { foreach ($value as $key => $val) { $value[$key] = $this->resolve($val); } return $value; } if (!\is_string($value)) { return $value; } $escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($value) { $parameter = $match[1] ?? null; // skip %% if (!isset($parameter)) { return '%%'; } if (preg_match('/^env\(\w+\)$/', $parameter)) { throw new RuntimeException(\sprintf('Using "%%%s%%" is not allowed in routing configuration.', $parameter)); } if (\array_key_exists($parameter, $this->collectedParameters)) { return $this->collectedParameters[$parameter]; } if ($this->container instanceof SymfonyContainerInterface) { $resolved = $this->container->getParameter($parameter); } else { $resolved = $this->container?->get($parameter); } if (\is_string($resolved) || is_numeric($resolved)) { $this->collectedParameters[$parameter] = $resolved; return (string) $resolved; } throw new RuntimeException(\sprintf('The container parameter "%s", used in the resource configuration value "%s", must be a string or numeric, but it is of type %s.', $parameter, $value, \gettype($resolved))); }, $value); return str_replace('%%', '%', $escapedValue ?? ''); } } ================================================ FILE: src/Component/src/Metadata/Extractor/PhpFileResourceExtractor.php ================================================ getPHPFileClosure($path)(); if (!$resource instanceof ResourceMetadata) { return; } $resourceReflection = new \ReflectionClass($resource); foreach ($resourceReflection->getProperties() as $property) { $property->setAccessible(true); $resolvedValue = $this->resolve($property->getValue($resource)); $property->setValue($resource, $resolvedValue); } $this->resources[] = $resource; } /** * Scope isolated include. * * Prevents access to $this/self from included files. */ private function getPHPFileClosure(string $filePath): \Closure { return \Closure::bind(function () use ($filePath): mixed { return require $filePath; }, null, null); } } ================================================ FILE: src/Component/src/Metadata/Extractor/ResourceExtractorInterface.php ================================================ |null $routeRequirements */ public function __construct( protected ?array $methods = null, protected ?string $path = null, protected ?string $routeName = null, protected ?string $routePrefix = null, protected ?array $routeRequirements = null, protected ?string $routeCondition = null, protected ?int $routePriority = null, ?string $template = null, ?string $shortName = null, ?string $name = null, string|callable|null $provider = null, string|callable|null $processor = null, string|callable|null $responder = null, string|callable|null $repository = null, ?string $repositoryMethod = null, ?array $repositoryArguments = null, ?bool $read = null, ?bool $write = null, ?bool $validate = null, ?bool $deserialize = null, ?bool $serialize = null, ?string $formType = null, ?array $formOptions = null, ?array $normalizationContext = null, ?array $denormalizationContext = null, ?array $validationContext = null, ?string $eventShortName = null, ?string $notificationMessage = null, string|\Stringable|null $security = null, ?string $securityMessage = null, string|callable|null $twigContextFactory = null, protected ?string $redirectTo = null, protected ?string $redirectToRoute = null, protected ?array $redirectArguments = null, protected ?array $vars = null, ) { parent::__construct( template: $template, shortName: $shortName, name: $name, provider: $provider, processor: $processor, responder: $responder, repository: $repository, repositoryMethod: $repositoryMethod, repositoryArguments: $repositoryArguments, read: $read, write: $write, validate: $validate, deserialize: $deserialize, serialize: $serialize, formType: $formType, formOptions: $formOptions, normalizationContext: $normalizationContext, denormalizationContext: $denormalizationContext, validationContext: $validationContext, eventShortName: $eventShortName, notificationMessage: $notificationMessage, security: $security, securityMessage: $securityMessage, ); $this->twigContextFactory = $twigContextFactory; } public function getMethods(): ?array { return $this->methods; } public function withMethods(array $methods): self { $self = clone $this; $self->methods = $methods; return $self; } public function getPath(): ?string { return $this->path; } public function withPath(string $path): self { $self = clone $this; $self->path = $path; return $self; } public function getRouteName(): ?string { return $this->routeName; } public function withRouteName(string $routeName): self { $self = clone $this; $self->routeName = $routeName; return $self; } public function getRoutePrefix(): ?string { return $this->routePrefix; } public function withRoutePrefix(?string $routePrefix): self { $self = clone $this; $self->routePrefix = $routePrefix; return $self; } /** * @return array|null */ public function getRouteRequirements(): ?array { return $this->routeRequirements; } /** * @param array|null $routeRequirements */ public function withRouteRequirements(?array $routeRequirements): self { $self = clone $this; $self->routeRequirements = $routeRequirements; return $self; } public function getRouteCondition(): ?string { return $this->routeCondition; } public function withRouteCondition(?string $routeCondition): self { $self = clone $this; $self->routeCondition = $routeCondition; return $self; } public function getRoutePriority(): ?int { return $this->routePriority; } public function withRoutePriority(?int $routePriority): self { $self = clone $this; $self->routePriority = $routePriority; return $self; } public function getTwigContextFactory(): callable|string|null { return $this->twigContextFactory; } public function withTwigContextFactory(callable|string|null $twigContextFactory): self { $self = clone $this; $self->twigContextFactory = $twigContextFactory; return $self; } public function getRedirectTo(): ?string { return $this->redirectTo; } public function withRedirectTo(?string $redirectTo): self { $self = clone $this; $self->redirectTo = $redirectTo; return $self; } public function getRedirectToRoute(): ?string { return $this->redirectToRoute; } public function withRedirectToRoute(string $redirectToRoute): self { $self = clone $this; $self->redirectToRoute = $redirectToRoute; return $self; } public function getRedirectArguments(): ?array { return $this->redirectArguments; } public function withRedirectArguments(array $redirectArguments): self { $self = clone $this; $self->redirectArguments = $redirectArguments; return $self; } public function getVars(): ?array { return $this->vars; } public function withVars(array $vars): self { $self = clone $this; $self->vars = $vars; return $self; } } ================================================ FILE: src/Component/src/Metadata/Index.php ================================================ grid; } public function withGrid(string $grid): self { $self = clone $this; $self->grid = $grid; return $self; } } ================================================ FILE: src/Component/src/Metadata/Inflector/Inflector.php ================================================ snake()->toString(); } public function pluralize(string $string): string { $pluralize = (new EnglishInflector())->pluralize($string); return $pluralize ? array_pop($pluralize) : ''; } public function dashize(string $string): string { return strtr($this->tableize($string), ['_' => '-']); } } ================================================ FILE: src/Component/src/Metadata/Inflector/InflectorInterface.php ================================================ name = $name; $this->applicationName = $applicationName; $this->driver = $parameters['driver']; $this->templatesNamespace = array_key_exists('templates', $parameters) ? $parameters['templates'] : null; $this->stateMachineComponent = $parameters['state_machine_component'] ?? null; $this->parameters = $parameters; } public static function fromAliasAndConfiguration(string $alias, array $parameters): self { [$applicationName, $name] = self::parseAlias($alias); return new self($name, $applicationName, $parameters); } public static function setInflector(InflectorObject $inflector): void { self::$inflectorInstance = $inflector; } private static function getInflector(): InflectorObject { if (self::$inflectorInstance === null) { $inflectorFactory = InflectorFactory::create(); self::$inflectorInstance = $inflectorFactory->build(); } return self::$inflectorInstance; } public function getAlias(): string { return $this->applicationName . '.' . $this->name; } public function getApplicationName(): string { return $this->applicationName; } public function getName(): string { return $this->name; } public function getHumanizedName(): string { return strtolower(trim((string) preg_replace(['/([A-Z])/', '/[_\s]+/'], ['_$1', ' '], $this->name))); } public function getPluralName(): string { return self::getInflector()->pluralize($this->name); } public function getDriver(): string|false { return $this->driver; } public function getStateMachineComponent(): ?string { return $this->stateMachineComponent; } public function getTemplatesNamespace(): ?string { return $this->templatesNamespace; } public function getParameter(string $name) { if (!$this->hasParameter($name)) { throw new \InvalidArgumentException(sprintf('Parameter "%s" is not configured for resource "%s".', $name, $this->getAlias())); } return $this->parameters[$name]; } public function hasParameter(string $name): bool { return array_key_exists($name, $this->parameters); } public function getParameters(): array { return $this->parameters; } public function getClass(string $name): string { if (!$this->hasClass($name)) { throw new \InvalidArgumentException(sprintf('Class "%s" is not configured for resource "%s".', $name, $this->getAlias())); } return $this->parameters['classes'][$name]; } public function hasClass(string $name): bool { return isset($this->parameters['classes'][$name]); } public function getServiceId(string $serviceName): string { return sprintf('%s.%s.%s', $this->applicationName, $serviceName, $this->name); } public function getPermissionCode(string $permissionName): string { return sprintf('%s.%s.%s', $this->applicationName, $this->name, $permissionName); } private static function parseAlias(string $alias): array { if (false === strpos($alias, '.')) { throw new \InvalidArgumentException(sprintf('Invalid alias "%s" supplied, it should conform to the following format ".".', $alias)); } return explode('.', $alias, 2); } } if (!class_exists(\Sylius\Component\Resource\Metadata\Metadata::class, false)) { class_alias(Metadata::class, \Sylius\Component\Resource\Metadata\Metadata::class); } ================================================ FILE: src/Component/src/Metadata/MetadataInterface.php ================================================ > */ private array $mutators = []; /** * Adds a mutator to the container for a given operation name. */ public function add(string $operationName, OperationMutatorInterface $mutator): void { $this->mutators[$operationName][] = $mutator; } public function get(string $id): array { return $this->mutators[$id] ?? []; } public function has(string $id): bool { return isset($this->mutators[$id]); } } ================================================ FILE: src/Component/src/Metadata/Mutator/OperationMutatorCollectionInterface.php ================================================ */ public function get(string $id): array; } ================================================ FILE: src/Component/src/Metadata/Mutator/ResourceMutatorCollection.php ================================================ > */ private array $mutators = []; public function add(string $resourceClass, ResourceMutatorInterface $mutator): void { $this->mutators[$resourceClass][] = $mutator; } public function get(string $id): array { return $this->mutators[$id] ?? []; } public function has(string $id): bool { return isset($this->mutators[$id]); } } ================================================ FILE: src/Component/src/Metadata/Mutator/ResourceMutatorCollectionInterface.php ================================================ */ public function get(string $id): array; } ================================================ FILE: src/Component/src/Metadata/Operation/DashPathSegmentNameGenerator.php ================================================ inflector->dashize($this->inflector->pluralize($name)) : $this->inflector->dashize($name); } } ================================================ FILE: src/Component/src/Metadata/Operation/HttpOperationInitiator.php ================================================ attributes->get('_route'); $syliusOptions = $attributes = $request->attributes->all('_sylius'); /** @var string|class-string|null $resource */ $resource = $attributes['resource'] ?? null; if ( [] === $syliusOptions || null === $resource || null === $operationName ) { return null; } if (str_contains($resource, '.')) { $metadata = $this->resourceRegistry->get($resource); } else { $metadata = $this->resourceRegistry->getByClass($resource); } $syliusOptions['resource_class'] = $metadata->getClass('model'); $request->attributes->set('_sylius', $syliusOptions); /** @var HttpOperation $operation */ $operation = $this->resourceMetadataCollectionFactory->create($metadata->getClass('model')) ->getOperation($metadata->getAlias(), $operationName) ; return $this->getOperationWithVars($operation); } private function getOperationWithVars(HttpOperation $operation): HttpOperation { $operationVars = $operation->getVars(); $resolvedOperationVars = $operationVars !== null ? $this->resolveVars($operationVars) : null; $resourceVars = $operation->getResource()?->getVars(); $resolvedResourceVars = $resourceVars !== null ? $this->resolveVars($resourceVars) : null; if (null === $resolvedOperationVars && null === $resolvedResourceVars) { return $operation; } $mergedVars = array_merge($resolvedResourceVars ?? [], $resolvedOperationVars ?? []); return $operation->withVars($mergedVars); } private function resolveVars(array $vars): array { if (null === $this->varsResolver) { return $vars; } return $this->varsResolver->resolve($vars); } } ================================================ FILE: src/Component/src/Metadata/Operation/HttpOperationInitiatorInterface.php ================================================ inflector->tableize($name); return $pluralize ? $this->inflector->pluralize($name) : $name; } } ================================================ FILE: src/Component/src/Metadata/Operation.php ================================================ provider = $provider; $this->processor = $processor; $this->responder = $responder; $this->repository = $repository; } public function getResource(): ?ResourceMetadata { return $this->resource; } public function withResource(ResourceMetadata $resource): self { $self = clone $this; $self->resource = $resource; return $self; } public function getTemplate(): ?string { return $this->template; } public function withTemplate(string $template): self { $self = clone $this; $self->template = $template; return $self; } public function getName(): ?string { return $this->name; } public function withName(string $name): self { $self = clone $this; $self->name = $name; return $self; } public function getShortName(): ?string { return $this->shortName; } public function withShortName(string $shortName): self { $self = clone $this; $self->shortName = $shortName; return $self; } public function getProvider(): callable|string|null { return $this->provider; } public function withProvider(string|callable|null $provider): self { $self = clone $this; $self->provider = $provider; return $self; } public function getProcessor(): callable|string|null { return $this->processor; } public function withProcessor(string|callable|null $processor): self { $self = clone $this; $self->processor = $processor; return $self; } public function getResponder(): callable|string|null { return $this->responder; } public function withResponder(string|callable|null $responder): self { $self = clone $this; $self->responder = $responder; return $self; } public function getRepository(): callable|string|null { return $this->repository; } public function withRepository(string|callable|null $repository): self { $self = clone $this; $self->repository = $repository; return $self; } public function getRepositoryMethod(): ?string { return $this->repositoryMethod; } public function withRepositoryMethod(string $repositoryMethod): self { $self = clone $this; $self->repositoryMethod = $repositoryMethod; return $self; } public function getRepositoryArguments(): ?array { return $this->repositoryArguments; } public function withRepositoryArguments(array $repositoryArguments): self { $self = clone $this; $self->repositoryArguments = $repositoryArguments; return $self; } public function canRead(): ?bool { return $this->read; } public function withRead(bool $read): self { $self = clone $this; $self->read = $read; return $self; } public function canWrite(): ?bool { return $this->write; } public function withWrite(bool $write): self { $self = clone $this; $self->write = $write; return $self; } public function canValidate(): ?bool { return $this->validate; } public function withValidate(bool $validate): self { $self = clone $this; $self->validate = $validate; return $self; } public function canDeserialize(): ?bool { return $this->deserialize; } public function withDeserialize(bool $deserialize): self { $self = clone $this; $self->deserialize = $deserialize; return $self; } public function canSerialize(): ?bool { return $this->serialize; } public function withSerialize(bool $serialize): self { $self = clone $this; $self->serialize = $serialize; return $self; } public function getFormType(): ?string { return $this->formType; } public function withFormType(string $formType): self { $self = clone $this; $self->formType = $formType; return $self; } public function getFormOptions(): ?array { return $this->formOptions; } public function withFormOptions(array $formOptions): self { $self = clone $this; $self->formOptions = $formOptions; return $self; } public function getNormalizationContext(): ?array { return $this->normalizationContext; } public function withNormalizationContext(?array $normalizationContext): self { $self = clone $this; $self->normalizationContext = $normalizationContext; return $self; } public function getDenormalizationContext(): ?array { return $this->denormalizationContext; } public function withDenormalizationContext(?array $denormalizationContext): self { $self = clone $this; $self->denormalizationContext = $denormalizationContext; return $self; } public function getValidationContext(): ?array { return $this->validationContext; } public function withValidationContext(?array $validationContext): self { $self = clone $this; $self->validationContext = $validationContext; return $self; } public function getEventShortName(): ?string { return $this->eventShortName; } public function withEventShortName(string $eventShortName): self { $self = clone $this; $self->eventShortName = $eventShortName; return $self; } public function getNotificationMessage(): ?string { return $this->notificationMessage; } public function withNotificationMessage(string $notificationMessage): self { $self = clone $this; $self->notificationMessage = $notificationMessage; return $self; } public function getSecurity(): ?string { return $this->security instanceof \Stringable ? (string) $this->security : $this->security; } public function withSecurity(string|\Stringable|null $security): static { $self = clone $this; $self->security = $security; return $self; } public function getSecurityMessage(): ?string { return $this->securityMessage; } public function withSecurityMessage(?string $securityMessage): static { $self = clone $this; $self->securityMessage = $securityMessage; return $self; } } ================================================ FILE: src/Component/src/Metadata/OperationAccessCheckerInterface.php ================================================ $operations */ public function __construct(array $operations = []) { foreach ($operations as $operationName => $operation) { $this->operations[] = [$operationName, $operation]; } } /** * @return \Iterator */ public function getIterator(): \Traversable { return (function (): \Generator { foreach ($this->operations as [$operationName, $operation]) { yield $operationName => $operation; } })(); } public function get(string $key): Operation { foreach ($this->operations as [$operationName, $operation]) { if ($operationName === $key) { return $operation; } } throw new \RuntimeException(sprintf('No Operation with key "%s" was found', $key)); } /** * @param T $value * * @return self */ public function add(string $key, Operation $value): self { foreach ($this->operations as $i => [$operationName, $operation]) { if ($operationName === $key) { $this->operations[$i] = [$key, $value]; return $this; } } $this->operations[] = [$key, $value]; return $this; } /** * @return self */ public function remove(string $key): self { foreach ($this->operations as $i => [$operationName, $operation]) { if ($operationName === $key) { unset($this->operations[$i]); return $this; } } throw new RuntimeException(sprintf('Could not remove operation "%s".', $key)); } public function has(string $key): bool { foreach ($this->operations as $i => [$operationName, $operation]) { if ($operationName === $key) { return true; } } return false; } public function count(): int { return \count($this->operations); } } ================================================ FILE: src/Component/src/Metadata/Registry.php ================================================ metadata; } public function get(string $alias): MetadataInterface { if (!array_key_exists($alias, $this->metadata)) { throw new \InvalidArgumentException(sprintf('Resource "%s" does not exist.', $alias)); } return $this->metadata[$alias]; } public function getByClass(string $className): MetadataInterface { foreach ($this->metadata as $metadata) { if ($className === $metadata->getClass('model')) { return $metadata; } } throw new \InvalidArgumentException(sprintf('Resource with model class "%s" does not exist.', $className)); } public function add(MetadataInterface $metadata): void { $this->metadata[$metadata->getAlias()] = $metadata; } public function addFromAliasAndConfiguration(string $alias, array $configuration): void { $this->add(Metadata::fromAliasAndConfiguration($alias, $configuration)); } } if (!class_exists(\Sylius\Component\Resource\Metadata\Registry::class, false)) { class_alias(Registry::class, \Sylius\Component\Resource\Metadata\Registry::class); } ================================================ FILE: src/Component/src/Metadata/RegistryInterface.php ================================================ decorated) { foreach ($this->decorated->create() as $resourceClass) { $classes[$resourceClass] = true; } } $paths = $this->mapping['paths'] ?? []; foreach (ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories($paths) as $reflectionClass) { $resourceClass = $reflectionClass->getName(); if ([] === ClassReflection::getClassAttributes($resourceClass, AsResource::class)) { continue; } $classes[$resourceClass] = true; } return new ResourceClassList(array_keys($classes)); } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php ================================================ buildResourceOperations($attributes, $resourceClass) as $resource) { $resourceMetadataCollection[] = $resource; } return $resourceMetadataCollection; } /** * @param \ReflectionAttribute[] $attributes * * @return ResourceMetadata[] */ private function buildResourceOperations(array $attributes, string $resourceClass): array { /** @var array $resources */ $resources = []; $index = -1; foreach ($attributes as $attribute) { if (is_a($attribute->getName(), AsResource::class, true)) { /** @var AsResource $resourceAttribute */ $resourceAttribute = $attribute->newInstance(); $resource = $resourceAttribute->toMetadata(); $resourceAlias = $resource->getAlias(); if (null !== $resourceAlias) { $resourceConfiguration = $this->resourceRegistry->get($resource->getAlias() ?? ''); } else { $resourceConfiguration = $this->resourceRegistry->getByClass($resourceClass); } $resource = $this->getResourceWithDefaults($resourceClass, $resource, $resourceConfiguration); $resources[++$index] = $resource; $operations = []; /** @var Operation $operation */ foreach ($resource->getOperations() ?? new Operations() as $operation) { [$key, $operation] = $this->getOperationWithDefaults($operation, $resources[$index], $this->operationRouteNameFactory, $this->resourceRegistry); $operations[$key] = $operation; } if ($operations) { $resources[$index] = $resources[$index]->withOperations(new Operations($operations)); } continue; } if (null === ($resources[$index] ?? null)) { try { $resourceConfiguration = $this->resourceRegistry->getByClass($resourceClass); $resource = new ResourceMetadata($resourceConfiguration->getAlias()); $resource = $this->getResourceWithDefaults($resourceClass, $resource, $resourceConfiguration); $resources[++$index] = $resource; } catch(\InvalidArgumentException) { } } if (!is_subclass_of($attribute->getName(), Operation::class)) { continue; } /** @var Operation $operationAttribute */ $operationAttribute = $attribute->newInstance(); [$key, $operation] = $this->getOperationWithDefaults($operationAttribute, $resources[$index], $this->operationRouteNameFactory, $this->resourceRegistry); $operations = $resources[$index]->getOperations() ?? new Operations(); $resources[$index] = $resources[$index]->withOperations($operations); $resources[$index] = $resources[$index]->withOperations($operations->add($key, $operation)); } return $resources; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/CachedResourceClassListFactory.php ================================================ cacheItemPool = $cacheItemPool; } /** * @inheritdoc */ public function create(): ResourceClassList { return $this->getCached(self::CACHE_KEY, fn (): ResourceClassList => $this->decorated->create()); } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/CachedResourceMetadataCollectionFactory.php ================================================ localCache)) { return new ResourceMetadataCollection($this->localCache[$cacheKey]); } try { $cacheItem = $this->cacheItemPool->getItem($cacheKey); } catch (CacheException) { $resourceMetadataCollection = $this->decorated->create($resourceClass); $this->localCache[$cacheKey] = (array) $resourceMetadataCollection; return $resourceMetadataCollection; } if ($cacheItem->isHit()) { $this->localCache[$cacheKey] = $cacheItem->get(); return new ResourceMetadataCollection($this->localCache[$cacheKey]); } $resourceMetadataCollection = $this->decorated->create($resourceClass); $this->localCache[$cacheKey] = (array) $resourceMetadataCollection; $cacheItem->set($this->localCache[$cacheKey]); $this->cacheItemPool->save($cacheItem); return $resourceMetadataCollection; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/EventShortNameResourceMetadataCollectionFactory.php ================================================ decorated->create($resourceClass); /** @var ResourceMetadata $resource */ foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) { $operations = $resource->getOperations() ?? new Operations(); /** @var Operation $operation */ foreach ($operations as $operation) { /** @var string $key */ $key = $operation->getName(); $operations->add($key, $this->addDefaults($operation)); } $resource = $resource->withOperations($operations); $resourceCollectionMetadata[$i] = $resource; } return $resourceCollectionMetadata; } private function addDefaults(Operation $operation): Operation { if (null === $operation->getEventShortName()) { $shortName = $operation instanceof ApplyStateMachineTransition ? ResourceActions::UPDATE : $operation->getShortName() ?? ''; $bulkPrefix = 'bulk_'; if (\str_starts_with($shortName, $bulkPrefix)) { $shortName = substr($shortName, strlen($bulkPrefix)); } $operation = $operation->withEventShortName($shortName); } return $operation; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/FactoryResourceMetadataCollectionFactory.php ================================================ decorated->create($resourceClass); /** @var ResourceMetadata $resource */ foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) { $resourceConfiguration = $this->resourceRegistry->get($resource->getAlias() ?? ''); $operations = $resource->getOperations() ?? new Operations(); /** @var Operation|(Operation&FactoryAwareOperationInterface) $operation */ foreach ($operations as $operation) { if (!$operation instanceof FactoryAwareOperationInterface) { continue; } /** @var string $key */ $key = $operation->getName(); /** @var Operation&FactoryAwareOperationInterface $operation */ $operation = $this->addDefaults($resourceConfiguration, $resource, $operation); $operations->add($key, $operation); } $resource = $resource->withOperations($operations); $resourceCollectionMetadata[$i] = $resource; } return $resourceCollectionMetadata; } private function addDefaults(MetadataInterface $resourceConfiguration, ResourceMetadata $resource, FactoryAwareOperationInterface $operation): FactoryAwareOperationInterface { if (null === $operation->getFactory() && str_starts_with($resourceConfiguration->getDriver() ?: '', 'doctrine')) { $operation = $operation->withFactory($resourceConfiguration->getServiceId('factory')); } if (null === $operation->getFactoryMethod()) { $operation = $operation->withFactoryMethod('createNew'); } return $operation; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/MutatorResourceMetadataCollectionFactory.php ================================================ decorated) { $resourceMetadataCollection = $this->decorated->create($resourceClass); } $newMetadataCollection = new ResourceMetadataCollection(); /** @var ResourceMetadata $resource */ foreach ($resourceMetadataCollection as $resource) { $resource = $this->mutateResource($resource, $resourceClass); $operations = $this->mutateOperations($resource->getOperations() ?? new Operations()); $resource = $resource->withOperations($operations); $newMetadataCollection[] = $resource; } return $newMetadataCollection; } private function mutateResource(ResourceMetadata $resource, string $resourceClass): ResourceMetadata { foreach ($this->resourceMutators->get($resourceClass) as $mutator) { $resource = $mutator($resource); } return $resource; } /** * @template T of Operation * * @param Operations $operations * * @return Operations */ private function mutateOperations(Operations $operations): Operations { $newOperations = new Operations(); foreach ($operations as $key => $operation) { foreach ($this->operationMutators->get($key) as $mutator) { $operation = $mutator($operation); } $newOperations->add($key, $operation); } return $newOperations; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/OperationDefaultsTrait.php ================================================ withClass($resourceClass); if (null === $resource->getAlias()) { $resource = $resource->withAlias($resourceConfiguration->getAlias()); } if (null === $resource->getApplicationName()) { $resource = $resource->withApplicationName($resourceConfiguration->getApplicationName()); } if (null === $resource->getName()) { $resource = $resource->withName($resourceConfiguration->getName()); } return $resource; } private function getOperationWithDefaults( Operation $operation, ResourceMetadata $resource, OperationRouteNameFactoryInterface $operationRouteNameFactory, RegistryInterface $resourceRegistry, ): array { $resourceConfiguration = $resourceRegistry->get($resource->getAlias() ?? ''); $operation = $operation->withResource($resource); if (null === $resource->getName()) { $resourceName = $resourceConfiguration->getName(); $resource = $resource->withName($resourceName); $operation = $operation->withResource($resource); } if (null === $resource->getPluralName()) { $resourcePluralName = $resourceConfiguration->getPluralName(); $resource = $resource->withPluralName($resourcePluralName); $operation = $operation->withResource($resource); } if (null === $operation->getNormalizationContext()) { $operation = $operation->withNormalizationContext($resource->getNormalizationContext()); } if (null === $operation->getDenormalizationContext()) { $operation = $operation->withDenormalizationContext($resource->getDenormalizationContext()); } if (null === $operation->getValidationContext()) { $operation = $operation->withValidationContext($resource->getValidationContext()); } $operation = $operation->withResource($resource); if (null === $operation->getRepository()) { $operation = $operation->withRepository($resourceConfiguration->getServiceId('repository')); } if (null === $operation->getFormType()) { $formType = $resource->getFormType(); $formType ??= $resourceConfiguration->hasClass('form') ? $resourceConfiguration->getClass('form') : null; if (null !== $formType) { $operation = $operation->withFormType($formType); } } if (null !== $operation->getFormType()) { $formOptions = $this->buildFormOptions($operation, $resourceConfiguration); $operation = $operation->withFormOptions($formOptions); } if ($operation instanceof HttpOperation) { if (null === $operation->getRoutePrefix()) { $operation = $operation->withRoutePrefix($resource->getRoutePrefix()); } if (null === $operation->getRouteRequirements()) { $operation = $operation->withRouteRequirements($resource->getRouteRequirements()); } if (null === $operation->getRouteCondition()) { $operation = $operation->withRouteCondition($resource->getRouteCondition()); } if (null === $operation->getRoutePriority()) { $operation = $operation->withRoutePriority($resource->getRoutePriority()); } if (null === $operation->getTwigContextFactory()) { $operation = $operation->withTwigContextFactory('sylius.twig.context.factory.default'); } if (null === $routeName = $operation->getRouteName()) { $routeName = $operationRouteNameFactory->createRouteName($operation); $operation = $operation->withRouteName($routeName); } if (null === $operation->getResponder()) { $operation = $operation->withResponder(Responder::class); } $operation = $operation->withName($routeName); } $operationName = $operation->getName(); return [$operationName, $operation]; } private function buildFormOptions(Operation $operation, MetadataInterface $resourceConfiguration): array { $formOptions = array_merge( ['data_class' => $resourceConfiguration->getClass('model')], $operation->getFormOptions() ?? [], ); $validationGroups = $operation->getValidationContext()['groups'] ?? null; if (null !== $validationGroups) { $formOptions = array_merge(['validation_groups' => $validationGroups], $formOptions); } return $formOptions; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/PhpFileResourceClassListFactory.php ================================================ decorated) { foreach ($this->decorated->create() as $resourceClass) { $classes[$resourceClass] = true; } } foreach ($this->phpFileResourceMetadataExtractor->getResources() as $resource) { $resourceClass = $resource->getClass(); if (null === $resourceClass) { continue; } $classes[$resourceClass] = true; } return new ResourceClassList(array_keys($classes)); } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/PhpFileResourceMetadataCollectionFactory.php ================================================ decorated) { $resourceMetadataCollection = $this->decorated->create($resourceClass); } foreach ($this->phpfileResourceMetadataExtractor->getResources() as $resource) { if ($resourceClass !== $resource->getClass()) { continue; } $resourceAlias = $resource->getAlias(); if (null !== $resourceAlias) { $resourceConfiguration = $this->resourceRegistry->get($resourceAlias); } else { $resourceConfiguration = $this->resourceRegistry->getByClass($resourceClass); } $resource = $this->getResourceWithDefaults($resourceClass, $resource, $resourceConfiguration); $operations = []; /** @var Operation $operation */ foreach ($resource->getOperations() ?? new Operations() as $operation) { [$key, $operation] = $this->getOperationWithDefaults($operation, $resource, $this->operationRouteNameFactory, $this->resourceRegistry); $operations[$key] = $operation; } if ($operations) { $resource = $resource->withOperations(new Operations($operations)); } $resourceMetadataCollection[] = $resource; } return $resourceMetadataCollection; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/PluralNameResourceMetadataCollectionFactory.php ================================================ decorated->create($resourceClass); /** @var ResourceMetadata $resource */ foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) { $resourceCollectionMetadata[$i] = $this->addDefaults($resource); } return $resourceCollectionMetadata; } private function addDefaults(ResourceMetadata $resource): ResourceMetadata { if (null !== $resource->getPluralName()) { return $resource; } if ($this->routingBcLayerEnabled) { if (null === $this->resourceRegistry) { throw new LogicException(sprintf( 'Routing Bc-Layer is enabled, but the resource registry is not passed as constructor arguments of "%s" class.', self::class, )); } $resourceConfiguration = $this->resourceRegistry->get($resource->getAlias() ?? ''); return $resource->withPluralName($resourceConfiguration->getPluralName()); } /** * Resource name has already been configured. * * @see OperationDefaultsTrait * * @var string $resourceName */ $resourceName = $resource->getName(); return $resource->withPluralName($this->inflector->pluralize($resourceName)); } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/ProviderResourceMetadataCollectionFactory.php ================================================ decorated->create($resourceClass); /** @var ResourceMetadata $resource */ foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) { $operations = $resource->getOperations() ?? new Operations(); /** @var Operation $operation */ foreach ($operations as $operation) { /** @var string $key */ $key = $operation->getName(); $operations->add($key, $this->addDefaults($operation)); } $resource = $resource->withOperations($operations); $resourceCollectionMetadata[$i] = $resource; } return $resourceCollectionMetadata; } private function addDefaults(Operation $operation): Operation { if ( null === $operation->getProvider() && $operation instanceof GridAwareOperationInterface && null !== $operation->getGrid() ) { $operation = $operation->withProvider(RequestGridProvider::class); } if (null === $operation->getProvider()) { $operation = $operation->withProvider(Provider::class); } return $operation; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/RedirectResourceMetadataCollectionFactory.php ================================================ decorated->create($resourceClass); /** @var ResourceMetadata $resource */ foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) { $operations = $resource->getOperations() ?? new Operations(); /** @var Operation $operation */ foreach ($operations as $operation) { if (!$operation instanceof HttpOperation) { continue; } /** @var string $key */ $key = $operation->getName(); $operations->add($key, $this->addDefaults($resource, $operation)); } $resource = $resource->withOperations($operations); $resourceCollectionMetadata[$i] = $resource; } return $resourceCollectionMetadata; } private function addDefaults(ResourceMetadata $resource, HttpOperation $operation): Operation { if (null !== $operation->getRedirectToRoute()) { return $operation; } if ($operation instanceof BulkOperationInterface) { $newOperation = $this->setRedirectIfRouteExists($resource, $operation, 'index'); if (null !== $newOperation) { return $newOperation; } } if ( $operation instanceof CreateOperationInterface || $operation instanceof UpdateOperationInterface ) { $newOperation = $this->setRedirectIfRouteExists($resource, $operation, 'show'); if (null !== $newOperation) { return $newOperation; } $newOperation = $this->setRedirectIfRouteExists($resource, $operation, 'index'); if (null !== $newOperation) { return $newOperation; } } if ($operation instanceof DeleteOperationInterface) { $newOperation = $this->setRedirectIfRouteExists($resource, $operation, 'index'); if (null !== $newOperation) { return $newOperation; } } return $operation; } private function setRedirectIfRouteExists(ResourceMetadata $resource, HttpOperation $operation, string $shortName): ?Operation { $routeName = $this->operationRouteNameFactory->createRouteName($operation, $shortName); if ($resource->hasOperation($routeName)) { return $operation->withRedirectToRoute($routeName); } return null; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/ResourceClassListFactoryInterface.php ================================================ decorated->create($resourceClass); /** @var ResourceMetadata $resource */ foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) { $resourceConfiguration = $this->resourceRegistry->get($resource->getAlias() ?? ''); $operations = $resource->getOperations() ?? new Operations(); /** @var Operation $operation */ foreach ($operations as $operation) { /** @var string $key */ $key = $operation->getName(); $operations->add($key, $this->addDefaults($resourceConfiguration, $operation)); } $resource = $resource->withOperations($operations); $resourceCollectionMetadata[$i] = $resource; } return $resourceCollectionMetadata; } private function addDefaults(MetadataInterface $resourceConfiguration, Operation $operation): Operation { if (!$operation instanceof StateMachineAwareOperationInterface) { return $operation; } if ( null === $operation->getStateMachineComponent() && method_exists($resourceConfiguration, 'getStateMachineComponent') ) { $stateMachineComponent = $resourceConfiguration->getStateMachineComponent() ?? $this->defaultStateMachineComponent; /** @var Operation $operation */ $operation = $operation->withStateMachineComponent($stateMachineComponent); } if ( method_exists($operation, 'getStateMachineTransition') && null !== $operation->getStateMachineTransition() && null === $operation->getProcessor() ) { $operation = $operation->withProcessor(ApplyStateMachineTransitionProcessor::class); } return $operation; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/TemplatesDirResourceMetadataCollectionFactory.php ================================================ decorated->create($resourceClass); /** @var ResourceMetadata $resource */ foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) { $operations = $resource->getOperations() ?? new Operations(); /** @var Operation $operation */ foreach ($operations as $operation) { /** @var string $key */ $key = $operation->getName(); $operations->add($key, $this->addDefaults($resource, $operation)); } $resource = $resource->withOperations($operations); $resourceCollectionMetadata[$i] = $resource; } return $resourceCollectionMetadata; } private function addDefaults(ResourceMetadata $resource, Operation $operation): Operation { if (null === $operation->getTemplate()) { $templateDir = $resource->getTemplatesDir() ?? $this->settings['default_templates_dir'] ?? ''; $template = sprintf('%s/%s.html.twig', $templateDir, $operation->getShortName() ?? ''); $operation = $operation->withTemplate($template); } return $operation; } } ================================================ FILE: src/Component/src/Metadata/Resource/Factory/VarsResourceMetadataCollectionFactory.php ================================================ decorated->create($resourceClass); /** @var ResourceMetadata $resource */ foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) { $operations = $resource->getOperations() ?? new Operations(); /** @var Operation $operation */ foreach ($operations as $operation) { if (!$operation instanceof HttpOperation) { continue; } /** @var string $key */ $key = $operation->getName(); $operations->add($key, $this->addDefaults($resource, $operation)); } $resource = $resource->withOperations($operations); $resourceCollectionMetadata[$i] = $resource; } return $resourceCollectionMetadata; } private function addDefaults(ResourceMetadata $resource, HttpOperation $operation): Operation { if (null === $resourceVars = $resource->getVars()) { return $operation; } return $operation->withVars(\array_merge($resourceVars, $operation->getVars() ?? [])); } } ================================================ FILE: src/Component/src/Metadata/Resource/ResourceClassList.php ================================================ */ public function getIterator(): \Traversable { return new \ArrayIterator($this->classes); } public function count(): int { return \count($this->classes); } } ================================================ FILE: src/Component/src/Metadata/Resource/ResourceMetadataCollection.php ================================================ getIterator() as $current) { if ( $current->getAlias() === $resourceAlias && $current->hasOperation($name) ) { return $current->getOperation($name); } } throw new \RuntimeException(sprintf( 'Operation "%s" for "%s" resource was not found.', $name, $resourceAlias, )); } } ================================================ FILE: src/Component/src/Metadata/ResourceMetadata.php ================================================ |null $routeRequirements */ public function __construct( private ?string $alias = null, private ?string $section = null, private ?string $formType = null, private ?string $templatesDir = null, private ?string $routePrefix = null, private ?array $routeRequirements = null, private ?string $routeCondition = null, private ?int $routePriority = null, private ?string $name = null, private ?string $pluralName = null, private ?string $applicationName = null, private ?string $identifier = null, private ?array $normalizationContext = null, private ?array $denormalizationContext = null, private ?array $validationContext = null, private ?string $class = null, private string|false|null $driver = null, private ?array $vars = null, ?array $operations = null, ) { $this->operations = null === $operations ? null : new Operations($operations); } public function getClass(): ?string { return $this->class; } public function withClass(string $class): self { $self = clone $this; $self->class = $class; return $self; } public function getAlias(): ?string { return $this->alias; } public function withAlias(string $alias): self { $self = clone $this; $self->alias = $alias; return $self; } public function getSection(): ?string { return $this->section; } public function withSection(string $section): self { $self = clone $this; $self->section = $section; return $self; } public function getFormType(): ?string { return $this->formType; } public function withFormType(string $formType): self { $self = clone $this; $self->formType = $formType; return $self; } public function getName(): ?string { return $this->name; } public function withName(string $name): self { $self = clone $this; $self->name = $name; return $self; } public function getPluralName(): ?string { return $this->pluralName; } public function withPluralName(string $pluralName): self { $self = clone $this; $self->pluralName = $pluralName; return $self; } public function getApplicationName(): ?string { return $this->applicationName; } public function withApplicationName(string $applicationName): self { $self = clone $this; $self->applicationName = $applicationName; return $self; } public function getTemplatesDir(): ?string { return $this->templatesDir; } public function withTemplatesDir(string $templatesDir): self { $self = clone $this; $self->templatesDir = $templatesDir; return $self; } public function getRoutePrefix(): ?string { return $this->routePrefix; } public function withRoutePrefix(string $routePrefix): self { $self = clone $this; $self->routePrefix = $routePrefix; return $self; } /** * @return array|null */ public function getRouteRequirements(): ?array { return $this->routeRequirements; } /** * @param array|null $routeRequirements */ public function withRouteRequirements(?array $routeRequirements): self { $self = clone $this; $self->routeRequirements = $routeRequirements; return $self; } public function getRouteCondition(): ?string { return $this->routeCondition; } public function withRouteCondition(?string $routeCondition): self { $self = clone $this; $self->routeCondition = $routeCondition; return $self; } public function getRoutePriority(): ?int { return $this->routePriority; } public function withRoutePriority(?int $routePriority): self { $self = clone $this; $self->routePriority = $routePriority; return $self; } public function getIdentifier(): ?string { return $this->identifier; } public function withIdentifier(string $identifier): self { $self = clone $this; $self->identifier = $identifier; return $self; } public function hasOperation(string $name): bool { return $this->operations?->has($name) ?? false; } public function getOperation(string $name): Operation { if (null === $operations = $this->operations) { throw new \RuntimeException(sprintf('No Operations were found on resource %s"', $this->alias ?? '')); } return $operations->get($name); } public function getOperations(): ?Operations { return $this->operations; } public function withOperations(Operations $operations): self { $self = clone $this; $self->operations = $operations; return $self; } public function getRouteName(string $shortName): string { $section = $this->getSection(); $sectionPrefix = $section ? $section . '_' : ''; return sprintf( '%s_%s%s_%s', $this->getApplicationName() ?? '', $sectionPrefix, $this->getName() ?? '', $shortName, ); } public function getNormalizationContext(): ?array { return $this->normalizationContext; } public function withNormalizationContext(?array $normalizationContext): self { $self = clone $this; $self->normalizationContext = $normalizationContext; return $self; } public function getDenormalizationContext(): ?array { return $this->denormalizationContext; } public function withDenormalizationContext(?array $denormalizationContext): self { $self = clone $this; $self->denormalizationContext = $denormalizationContext; return $self; } public function getValidationContext(): ?array { return $this->validationContext; } public function withValidationContext(?array $validationContext): self { $self = clone $this; $self->validationContext = $validationContext; return $self; } public function getDriver(): false|string|null { return $this->driver; } public function withDriver(false|string $driver): self { $self = clone $this; $self->driver = $driver; return $self; } public function getVars(): ?array { return $this->vars; } public function withVars(array $vars): self { $self = clone $this; $self->vars = $vars; return $self; } } ================================================ FILE: src/Component/src/Metadata/ResourceMutatorInterface.php ================================================ stateMachineComponent; } public function withStateMachineComponent(?string $stateMachineComponent): self { $self = clone $this; $self->stateMachineComponent = $stateMachineComponent; return $self; } public function getStateMachineTransition(): ?string { return $this->stateMachineTransition; } public function withStateMachineTransition(string $stateMachineTransition): self { $self = clone $this; $self->stateMachineTransition = $stateMachineTransition; return $self; } public function getStateMachineGraph(): ?string { return $this->stateMachineGraph; } public function withStateMachineGraph(string $stateMachineGraph): self { $self = clone $this; $self->stateMachineGraph = $stateMachineGraph; return $self; } } ================================================ FILE: src/Component/src/Metadata/UpdateOperationInterface.php ================================================ */ private array $localCache = []; private function getCached(string $cacheKey, callable $getValue): mixed { if (\array_key_exists($cacheKey, $this->localCache)) { return $this->localCache[$cacheKey]; } try { $cacheItem = $this->cacheItemPool->getItem($cacheKey); } catch (CacheException) { return $this->localCache[$cacheKey] = $getValue(); } if ($cacheItem->isHit()) { return $this->localCache[$cacheKey] = $cacheItem->get(); } $value = $getValue(); $cacheItem->set($value); $this->cacheItemPool->save($cacheItem); return $this->localCache[$cacheKey] = $value; } } ================================================ FILE: src/Component/src/Model/AbstractTranslation.php ================================================ translatable; // Return typehint should account for null value. Assert::notNull($translatable); return $translatable; } public function setTranslatable(?TranslatableInterface $translatable): void { if ($translatable === $this->translatable) { return; } $previousTranslatable = $this->translatable; $this->translatable = $translatable; if (null !== $previousTranslatable) { $previousTranslatable->removeTranslation($this); } if (null !== $translatable) { $translatable->addTranslation($this); } } public function getLocale(): ?string { return $this->locale; } public function setLocale(?string $locale): void { $this->locale = $locale; } } if (!class_exists(\Sylius\Component\Resource\Model\AbstractTranslation::class, false)) { class_alias(AbstractTranslation::class, \Sylius\Component\Resource\Model\AbstractTranslation::class); } ================================================ FILE: src/Component/src/Model/ArchivableInterface.php ================================================ archivedAt; } public function setArchivedAt(?\DateTimeInterface $archivedAt): void { $this->archivedAt = $archivedAt; } } if (!class_exists(\Sylius\Component\Resource\Model\ArchivableTrait::class, false)) { class_alias(ArchivableTrait::class, \Sylius\Component\Resource\Model\ArchivableTrait::class); } ================================================ FILE: src/Component/src/Model/CodeAwareInterface.php ================================================ createdAt; } public function setCreatedAt(?\DateTimeInterface $createdAt): void { $this->createdAt = $createdAt; } public function getUpdatedAt(): ?\DateTimeInterface { return $this->updatedAt; } public function setUpdatedAt(?\DateTimeInterface $updatedAt): void { $this->updatedAt = $updatedAt; } } if (!class_exists(\Sylius\Component\Resource\Model\TimestampableTrait::class, false)) { class_alias(TimestampableTrait::class, \Sylius\Component\Resource\Model\TimestampableTrait::class); } ================================================ FILE: src/Component/src/Model/ToggleableInterface.php ================================================ enabled; } public function setEnabled(?bool $enabled): void { $this->enabled = (bool) $enabled; } public function enable(): void { $this->enabled = true; } public function disable(): void { $this->enabled = false; } } if (!class_exists(\Sylius\Component\Resource\Model\ToggleableTrait::class, false)) { class_alias(ToggleableTrait::class, \Sylius\Component\Resource\Model\ToggleableTrait::class); } ================================================ FILE: src/Component/src/Model/TranslatableInterface.php ================================================ */ public function getTranslations(): Collection; public function getTranslation(?string $locale = null): TranslationInterface; public function hasTranslation(TranslationInterface $translation): bool; public function addTranslation(TranslationInterface $translation): void; public function removeTranslation(TranslationInterface $translation): void; public function setCurrentLocale(string $locale): void; public function setFallbackLocale(string $locale): void; } if (!class_exists(\Sylius\Component\Resource\Model\TranslatableInterface::class, false)) { class_alias(TranslatableInterface::class, \Sylius\Component\Resource\Model\TranslatableInterface::class); } ================================================ FILE: src/Component/src/Model/TranslatableTrait.php ================================================ translations = new ArrayCollection(); } public function getTranslation(?string $locale = null): TranslationInterface { $locale = $locale ?: $this->currentLocale; if (null === $locale) { throw new \RuntimeException('No locale has been set and current locale is undefined.'); } if (isset($this->translationsCache[$locale])) { return $this->translationsCache[$locale]; } $translation = $this->translations->get($locale); if (null !== $translation) { $this->translationsCache[$locale] = $translation; return $translation; } if ($locale !== $this->fallbackLocale) { if (isset($this->translationsCache[$this->fallbackLocale])) { return $this->translationsCache[$this->fallbackLocale]; } $fallbackTranslation = $this->translations->get($this->fallbackLocale); if (null !== $fallbackTranslation) { $this->translationsCache[$this->fallbackLocale] = $fallbackTranslation; return $fallbackTranslation; } } $translation = $this->createTranslation(); $translation->setLocale($locale); $this->addTranslation($translation); $this->translationsCache[$locale] = $translation; return $translation; } /** * @return Collection|TranslationInterface[] */ public function getTranslations(): Collection { return $this->translations; } public function hasTranslation(TranslationInterface $translation): bool { return isset($this->translationsCache[$translation->getLocale()]) || $this->translations->containsKey($translation->getLocale()); } public function addTranslation(TranslationInterface $translation): void { if (!$this->hasTranslation($translation)) { $this->translationsCache[$translation->getLocale()] = $translation; $this->translations->set($translation->getLocale(), $translation); $translation->setTranslatable($this); } } public function removeTranslation(TranslationInterface $translation): void { if ($this->translations->removeElement($translation)) { unset($this->translationsCache[$translation->getLocale()]); $translation->setTranslatable(null); } } public function setCurrentLocale(string $currentLocale): void { $this->currentLocale = $currentLocale; } public function setFallbackLocale(string $fallbackLocale): void { $this->fallbackLocale = $fallbackLocale; } /** * Create resource translation model. */ abstract protected function createTranslation(): TranslationInterface; } if (!class_exists(\Sylius\Component\Resource\Model\TranslatableTrait::class, false)) { class_alias(TranslatableTrait::class, \Sylius\Component\Resource\Model\TranslatableTrait::class); } ================================================ FILE: src/Component/src/Model/TranslationInterface.php ================================================ 1 ? new \ReflectionMethod($object, $callableParts[1]) : new \ReflectionFunction($callable); } if (!is_array($callable)) { $callable = [$callable, '__invoke']; } return new \ReflectionMethod($callable[0], $callable[1]); } } ================================================ FILE: src/Component/src/Reflection/ClassInfoTrait.php ================================================ getRealClassName($object::class); } /** * Get the real class name of a class name that could be a proxy. * * @param class-string $className * * @return class-string */ private function getRealClassName(string $className): string { // __CG__: Doctrine Common Marker for Proxy (ODM < 2.0 and ORM < 3.0) // __PM__: Ocramius Proxy Manager (ODM >= 2.0) $positionCg = strrpos($className, '\\__CG__\\'); $positionPm = strrpos($className, '\\__PM__\\'); if (false === $positionCg && false === $positionPm) { return $className; } if (false !== $positionCg) { $unProxiedClassName = substr($className, $positionCg + 8); Assert::classExists($unProxiedClassName); return $unProxiedClassName; } $className = ltrim($className, '\\'); $unProxiedClassName = substr( $className, 8 + $positionPm, strrpos($className, '\\') - ($positionPm + 8), ); Assert::classExists($unProxiedClassName); return $unProxiedClassName; } } ================================================ FILE: src/Component/src/Reflection/ClassReflection.php ================================================ */ public static function getResourcesByPaths(array $paths): iterable { trigger_deprecation('sylius/resource-bundle', '1.14', 'The method "%s" is deprecated, use "%s::%s" instead.', __METHOD__, ReflectionClassRecursiveIterator::class, 'getReflectionClassesFromDirectories'); foreach (ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories($paths) as $reflectionClass) { yield $reflectionClass->getName(); } } public static function getResourcesByPath(string $path): iterable { trigger_deprecation('sylius/resource-bundle', '1.14', 'The method "%s" is deprecated, use "%s::%s" instead.', __METHOD__, ReflectionClassRecursiveIterator::class, 'getReflectionClassesFromDirectories'); foreach (ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories([$path]) as $reflectionClass) { yield $reflectionClass->getName(); } } /** * @param class-string $className * * @return \ReflectionAttribute[] */ public static function getClassAttributes(string $className, ?string $attributeName = null): array { $reflectionClass = new \ReflectionClass($className); /** @psalm-suppress ArgumentTypeCoercion */ return $reflectionClass->getAttributes($attributeName); } } if (!class_exists(\Sylius\Component\Resource\Reflection\ClassReflection::class, false)) { class_alias(ClassReflection::class, \Sylius\Component\Resource\Reflection\ClassReflection::class); } ================================================ FILE: src/Component/src/Reflection/Filter/FunctionArgumentsFilter.php ================================================ getParameters() as $param) { $arguments[$param->name] = null; } return $arguments; } } ================================================ FILE: src/Component/src/Reflection/ReflectionClassRecursiveIterator.php ================================================ >> */ private static array $localCache; private function __construct() { } /** * @param string[] $directories * * @return array> */ public static function getReflectionClassesFromDirectories(array $directories, string $ignoreRegex = ''): array { $id = hash('xxh3', implode('', $directories) . $ignoreRegex); if (isset(self::$localCache[$id])) { return self::$localCache[$id]; } $includedFiles = []; foreach ($directories as $path) { $iterator = new \RegexIterator( new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), \RecursiveIteratorIterator::LEAVES_ONLY, ), '/^' . $ignoreRegex . '.+\.php$/i', \RecursiveRegexIterator::GET_MATCH, ); foreach ($iterator as $file) { /** @var array{0: string} $file */ $sourceFile = $file[0]; if (!preg_match('(^phar:)i', (string) $sourceFile)) { $sourceFile = realpath($sourceFile); } try { require_once $sourceFile; } catch (\Throwable) { // invalid PHP file (example: missing parent class) continue; } $includedFiles[$sourceFile] = true; } } $sortedClasses = get_declared_classes(); sort($sortedClasses); $sortedInterfaces = get_declared_interfaces(); sort($sortedInterfaces); $declared = [...$sortedClasses, ...$sortedInterfaces]; $ret = []; foreach ($declared as $className) { $reflectionClass = new \ReflectionClass($className); $sourceFile = $reflectionClass->getFileName(); if (isset($includedFiles[$sourceFile])) { $ret[$className] = $reflectionClass; } } return self::$localCache[$id] = $ret; } } ================================================ FILE: src/Component/src/ResourceActions.php ================================================ getFactory(); if (!$factory) { return null; } $arguments = $this->parseArgumentValues($operation->getFactoryArguments() ?? []); if (\is_callable($factory)) { return $factory(...$arguments); } if (!$this->locator->has($factory)) { throw new \RuntimeException(sprintf('Factory "%s" not found on operation "%s"', $factory, $operation->getName() ?? '')); } $factoryInstance = $this->locator->get($factory); Assert::isInstanceOf($factoryInstance, ResourceFactoryInterface::class); $factoryMethod = $operation->getFactoryMethod(); if (null === $factoryMethod) { throw new \RuntimeException(sprintf('No Factory method was configured on operation "%s"', $operation->getName() ?? '')); } return $factoryInstance->$factoryMethod(...$arguments); } private function parseArgumentValues(array $arguments): array { foreach ($arguments as $key => $value) { if (!str_starts_with($value, '@=')) { $value = '@=' . $value; trigger_deprecation('sylius/resource-bundle', '1.14', 'You passed "%s" as a string value in your repository arguments. If this is a value that needs to be parsed using the expression language, please prefix your string with "@=". In your case, use "@=%s"."', $value, $value); } // Not reachable as long as the BC layer above is there if (!str_starts_with($value, '@=')) { $arguments[$key] = $value; continue; } $value = substr($value, 2); $arguments[$key] = $this->argumentParser->parseExpression($value); } return $arguments; } } ================================================ FILE: src/Component/src/State/FactoryInterface.php ================================================ processor->process($data, $operation, $context); } foreach ($data as $item) { $this->processor->process($item, $operation, $context); } return null; } } ================================================ FILE: src/Component/src/State/Processor/EventDispatcherBulkAwareProcessor.php ================================================ operationEventDispatcher->dispatchBulkEvent($data, $operation, $context); } return $this->decorated->process($data, $operation, $context); } } ================================================ FILE: src/Component/src/State/Processor/FlashProcessor.php ================================================ get(RequestOption::class)?->request(); if (null === $request) { return $this->processor->process($data, $operation, $context); } $format = $request->getRequestFormat(); if ( $data instanceof Response || $request->isMethodSafe() || $format !== 'html' || !($operation->canWrite() ?? true) ) { return $this->processor->process($data, $operation, $context); } $this->addFlash($request, $operation, $context); return $this->processor->process($data, $operation, $context); } private function addFlash(Request $request, Operation $operation, Context $context): void { if ($request->attributes->has('error')) { $this->flashHelper->addErrorFlash($operation, $context); return; } $this->flashHelper->addSuccessFlash($operation, $context); } } ================================================ FILE: src/Component/src/State/Processor/RespondProcessor.php ================================================ responder->respond($data, $operation, $context); Assert::isInstanceOf($response, Response::class); return $response; } } ================================================ FILE: src/Component/src/State/Processor/WriteProcessor.php ================================================ canWrite() ?? true) || !$operation->getProcessor() ) { return $this->processor->process($data, $operation, $context); } try { return $this->processor->process($this->locatorProcessor->process($data, $operation, $context), $operation, $context); } catch (WriteResourceException $exception) { $request = $context->get(RequestOption::class)?->request(); $request?->attributes->set('error', $exception->getMessage()); return $this->processor->process(null, $operation, $context); } } } ================================================ FILE: src/Component/src/State/Processor.php ================================================ getProcessor(); if (null === $processor) { return null; } if (\is_callable($processor)) { return $processor($data, $operation, $context); } if (!$this->locator->has($processor)) { throw new \RuntimeException(sprintf('Processor "%s" not found on operation "%s"', $processor, $operation->getName() ?? '')); } /** @var ProcessorInterface $processorInstance */ $processorInstance = $this->locator->get($processor); Assert::isInstanceOf($processorInstance, ProcessorInterface::class); return $processorInstance->process($data, $operation, $context); } } ================================================ FILE: src/Component/src/State/ProcessorInterface.php ================================================ decorated->provide($operation, $context); $request = $context->get(RequestOption::class)?->request(); if ( !$operation instanceof FactoryAwareOperationInterface || !($operation->getFactory() ?? true) ) { return $data; } $data = $this->factory->create($operation, $context); $request?->attributes->set('data', $data); return $data; } } ================================================ FILE: src/Component/src/State/Provider/ReadProvider.php ================================================ get(RequestOption::class)?->request(); if ( $operation instanceof CreateOperationInterface || !($operation->canRead() ?? true) ) { return null; } $data = $this->provider->provide($operation, $context); if (null === $data) { throw new NotFoundHttpException('Resource has not been found.'); } $request?->attributes->set('data', $data); return $data; } } ================================================ FILE: src/Component/src/State/Provider/SecurityProvider.php ================================================ provider->provide($operation, $context); if ($this->operationAccessChecker->isGranted($operation, $context, ['object' => $data])) { return $data; } $exception = new AccessDeniedException($operation->getSecurityMessage() ?? 'Access denied.'); $exception->setAttributes([ 'operation' => $operation, 'context' => $context, ]); $exception->setSubject($data); throw $exception; } } ================================================ FILE: src/Component/src/State/Provider.php ================================================ getProvider(); if (null === $provider) { return null; } if (\is_callable($provider)) { return $provider($operation, $context); } if (!$this->locator->has($provider)) { throw new \RuntimeException(sprintf('Provider "%s" not found on operation "%s"', $provider, $operation->getName() ?? '')); } $providerInstance = $this->locator->get($provider); Assert::isInstanceOf($providerInstance, ProviderInterface::class); return $providerInstance->provide($operation, $context); } } ================================================ FILE: src/Component/src/State/ProviderInterface.php ================================================ getResponder(); if (null === $responder) { return null; } if (\is_callable($responder)) { return $responder($data, $operation, $context); } if (!$this->locator->has($responder)) { throw new \RuntimeException(sprintf('Responder "%s" not found on operation "%s"', $responder, $operation->getName() ?? '')); } $responderInstance = $this->locator->get($responder); Assert::isInstanceOf($responderInstance, ResponderInterface::class); return $responderInstance->respond($data, $operation, $context); } } ================================================ FILE: src/Component/src/State/ResponderInterface.php ================================================ getStateMachine($operation); return $stateMachine?->can($data, $operation, $context) ?? false; } public function apply(object $data, Operation $operation, Context $context): void { $stateMachine = $this->getStateMachine($operation); $stateMachine?->apply($data, $operation, $context); } private function getStateMachine(Operation $operation): ?OperationStateMachineInterface { Assert::isInstanceOf($operation, StateMachineAwareOperationInterface::class); $stateMachine = $operation->getStateMachineComponent(); if (null === $stateMachine) { return null; } if (!$this->locator->has($stateMachine)) { throw new \RuntimeException(sprintf('State machine "%s" not found on operation "%s"', $stateMachine, $operation->getName() ?? '')); } $stateMachineInstance = $this->locator->get($stateMachine); Assert::isInstanceOf($stateMachineInstance, OperationStateMachineInterface::class); return $stateMachineInstance; } } ================================================ FILE: src/Component/src/StateMachine/OperationStateMachineInterface.php ================================================ stateMachine->can($data, $operation, $context)) { $this->stateMachine->apply($data, $operation, $context); } return $this->writeProcessor?->process($data, $operation, $context); } } ================================================ FILE: src/Component/src/StateMachine/StateMachine.php ================================================ getPossibleTransitions() as $transition) { $config = $this->config['transitions'][$transition]; if (in_array($fromState, $config['from'], true)) { return $transition; } } return null; } public function getTransitionToState(string $toState): ?string { foreach ($this->getPossibleTransitions() as $transition) { $config = $this->config['transitions'][$transition]; if ($toState === $config['to']) { return $transition; } } return null; } } if (!class_exists(\Sylius\Component\Resource\StateMachine\StateMachine::class, false)) { class_alias(StateMachine::class, \Sylius\Component\Resource\StateMachine\StateMachine::class); } ================================================ FILE: src/Component/src/StateMachine/StateMachineInterface.php ================================================ operationInitiator->initializeOperation($request); if (null === $operation) { throw new RuntimeException('Operation should not be null.'); } $context = $this->requestContextInitiator->initializeContext($request); if (null === $operation->canWrite()) { $operation = $operation->withWrite(!$request->isMethodSafe()); } $data = $this->provider->provide($operation, $context); $valid = $request->attributes->getBoolean('is_valid', true); if (!$valid) { $operation = $operation->withWrite(false); } return $this->processor->process($data, $operation, $context); } } ================================================ FILE: src/Component/src/Symfony/DependencyInjection/Compiler/DisableMetadataCachePass.php ================================================ hasParameter('kernel.debug') || !$container->getParameter('kernel.debug') ) { return; } $container->removeDefinition('sylius.resource_metadata_collection.factory.cached'); $container->removeDefinition('sylius.metadata.resource_class_list.cached'); } } ================================================ FILE: src/Component/src/Symfony/DependencyInjection/Compiler/FallbackToKernelDefaultLocalePass.php ================================================ hasParameter('locale') || !$container->hasParameter('kernel.default_locale') ) { return; } /** @var string $locale */ $locale = $container->getParameter('kernel.default_locale'); $this->replaceLocaleInFlashHelper($container, $locale); $this->replaceLocaleProvider($container, $locale); } private function replaceLocaleInFlashHelper(ContainerBuilder $container, string $locale): void { if (!$container->hasDefinition('sylius.resource_controller.flash_helper')) { return; } $flashHelper = $container->getDefinition('sylius.resource_controller.flash_helper'); $flashHelper->replaceArgument(2, $locale); } private function replaceLocaleProvider(ContainerBuilder $container, string $locale): void { if (!$container->hasDefinition('sylius.translation_locale_provider.immutable')) { return; } $localeProvider = $container->getDefinition('sylius.translation_locale_provider.immutable'); $localeProvider->replaceArgument(0, [$locale]); $localeProvider->replaceArgument(1, $locale); } } ================================================ FILE: src/Component/src/Symfony/DependencyInjection/Compiler/MetadataMutatorPass.php ================================================ processResourceMutators($container); $this->processOperationMutators($container); } public function processResourceMutators(ContainerBuilder $container): void { if (!$container->hasDefinition('sylius.metadata.mutator_collection.resource')) { return; } $definition = $container->getDefinition('sylius.metadata.mutator_collection.resource'); $mutators = $container->findTaggedServiceIds('sylius.resource_mutator'); foreach ($mutators as $id => $tags) { foreach ($tags as $tag) { $definition->addMethodCall('add', [ $tag['resourceClass'], new Reference($id), ]); } } } private function processOperationMutators(ContainerBuilder $container): void { if (!$container->hasDefinition('sylius.metadata.mutator_collection.operation')) { return; } $definition = $container->getDefinition('sylius.metadata.mutator_collection.operation'); $mutators = $container->findTaggedServiceIds('sylius.operation_mutator'); foreach ($mutators as $id => $tags) { foreach ($tags as $tag) { $definition->addMethodCall('add', [ $tag['operationName'], new Reference($id), ]); } } } } ================================================ FILE: src/Component/src/Symfony/EventDispatcher/GenericEvent.php ================================================ messageType = $type; $this->message = $message; $this->messageParameters = $parameters; $this->errorCode = $errorCode; $this->stopPropagation(); } public function isStopped(): bool { return $this->isPropagationStopped(); } public function getMessageType(): string { return $this->messageType; } /** * @param string $messageType Should be one of ResourceEvent's TYPE constants */ public function setMessageType($messageType): void { $this->messageType = $messageType; } public function getMessage(): string { return $this->message; } public function setMessage(string $message): void { $this->message = $message; } public function getMessageParameters(): array { return $this->messageParameters; } public function setMessageParameters(array $messageParameters): void { $this->messageParameters = $messageParameters; } public function getErrorCode(): int { return $this->errorCode; } public function setErrorCode(int $errorCode): void { $this->errorCode = $errorCode; } public function setResponse(Response $response): void { $this->response = $response; } public function hasResponse(): bool { return null !== $this->response; } public function getResponse(): ?Response { return $this->response; } } if (!class_exists(\Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent::class, false)) { class_alias(GenericEvent::class, \Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent::class); } ================================================ FILE: src/Component/src/Symfony/EventDispatcher/OperationEvent.php ================================================ getArgument('operation'); return $operation; } public function getContext(): Context { /** @var Context $context */ $context = $this->getArgument('context'); return $context; } } ================================================ FILE: src/Component/src/Symfony/EventDispatcher/OperationEventDispatcher.php ================================================ dispatchEvent($data, $operation, $context); } public function dispatchBulkEvent(mixed $data, Operation $operation, Context $context): OperationEvent { return $this->dispatchEvent($data, $operation, $context, 'bulk'); } public function dispatchPreEvent(mixed $data, Operation $operation, Context $context): OperationEvent { return $this->dispatchEvent($data, $operation, $context, 'pre'); } public function dispatchPostEvent(mixed $data, Operation $operation, Context $context): OperationEvent { return $this->dispatchEvent($data, $operation, $context, 'post'); } public function dispatchInitializeEvent(mixed $data, Operation $operation, Context $context): OperationEvent { return $this->dispatchEvent($data, $operation, $context, 'initialize'); } private function dispatchEvent(mixed $data, Operation $operation, Context $context, ?string $eventType = null): OperationEvent { $operationEvent = new OperationEvent($data, ['operation' => $operation, 'context' => $context]); $resource = $operation->getResource(); if (null === $resource) { return $operationEvent; } $eventName = sprintf( '%s.%s.%s%s', $resource->getApplicationName() ?? '', $resource->getName() ?? '', $eventType ? $eventType . '_' : '', $operation->getEventShortName() ?? '', ); $this->eventDispatcher->dispatch($operationEvent, $eventName); return $operationEvent; } } ================================================ FILE: src/Component/src/Symfony/EventDispatcher/OperationEventDispatcherInterface.php ================================================ isStopped()) { return null; } $request = $context->get(RequestOption::class)?->request(); if ('html' !== $request?->getRequestFormat()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } $this->flashHelper->addFlashFromEvent($event, $context); if (null !== $operationEventResponse = $event->getResponse()) { return $operationEventResponse; } $operation = $event->getOperation(); if ($operation instanceof HttpOperation && null !== $request) { if (null === $newOperation) { return $this->redirectHandler->redirectToResource($event->getSubject(), $operation, $request); } return $this->redirectHandler->redirectToOperation($event->getSubject(), $operation, $request, $newOperation); } return null; } public function handlePostProcessEvent( OperationEvent $event, Context $context, ): ?Response { $request = $context->get(RequestOption::class)?->request(); if ('html' !== $request?->getRequestFormat()) { return null; } return $event->getResponse(); } } ================================================ FILE: src/Component/src/Symfony/EventDispatcher/OperationEventHandlerInterface.php ================================================ provider->provide($operation, $context); if ( $operation instanceof CollectionOperationInterface || $operation instanceof ShowOperationInterface ) { $this->operationEventDispatcher->dispatch(null, $operation, $context); } return $data; } } ================================================ FILE: src/Component/src/Symfony/EventDispatcher/State/DispatchPostWriteEventProcessor.php ================================================ processor->process($data, $operation, $context); if ($data instanceof Response) { return $data; } $operationEvent = $this->operationEventDispatcher->dispatchPostEvent($data, $operation, $context); $eventResponse = $this->eventHandler->handlePostProcessEvent( $operationEvent, $context, ); if (null !== $eventResponse) { return $eventResponse; } return $data; } } ================================================ FILE: src/Component/src/Symfony/EventDispatcher/State/DispatchPreWriteEventProcessor.php ================================================ operationEventDispatcher->dispatchPreEvent($data, $operation, $context); $eventResponse = $this->eventHandler->handlePreProcessEvent( $operationEvent, $context, $operation instanceof CreateOperationInterface ? ResourceActions::INDEX : null, ); if (null !== $eventResponse) { return $eventResponse; } return $this->processor->process($data, $operation, $context); } } ================================================ FILE: src/Component/src/Symfony/EventListener/AddFormatListener.php ================================================ getRequest(); $operation = $this->operationInitiator->initializeOperation($request); if ( null === $operation ) { return; } $mimeTypes = ['text/html', 'application/json', 'application/xml']; // First, try to guess the format from the Accept header $accept = $request->headers->get('Accept'); if (null !== $accept) { /** @var BaseAccept|null $mediaType */ $mediaType = $this->negotiator->getBest($accept, $mimeTypes); if (null !== $mediaType) { $request->setRequestFormat($request->getFormat($mediaType->getType())); return; } } // Then use the Symfony request format if available and applicable $requestFormat = $request->getRequestFormat(null); if (null !== $requestFormat) { $mimeType = $request->getMimeType($requestFormat); if (\in_array($mimeType, $mimeTypes, true)) { return; } throw $this->getNotAcceptableHttpException($mimeType ?? '', $mimeTypes); } } /** * Retrieves an instance of NotAcceptableHttpException. */ private function getNotAcceptableHttpException(string $accept, array $mimeTypes): NotAcceptableHttpException { return new NotAcceptableHttpException(sprintf( 'Requested format "%s" is not supported. Supported MIME types are "%s".', $accept, implode('", "', $mimeTypes), )); } } ================================================ FILE: src/Component/src/Symfony/ExpressionLanguage/ArgumentParser.php ================================================ expressionLanguage->registerProvider($provider); } } public function parseExpression(string $expression, array $variables = []): mixed { return $this->expressionLanguage->evaluate( $expression, array_merge( $this->variablesCollection->getVariables(), $variables, ), ); } } ================================================ FILE: src/Component/src/Symfony/ExpressionLanguage/ArgumentParserInterface.php ================================================ $this->requestStack->getCurrentRequest(), ]; } } ================================================ FILE: src/Component/src/Symfony/ExpressionLanguage/SyliusRepositoriesVariables.php ================================================ $this->repositories, ]; } } ================================================ FILE: src/Component/src/Symfony/ExpressionLanguage/TokenVariables.php ================================================ tokenStorage) { throw new \LogicException('The "symfony/security-bundle" must be installed and configured to use the "token" & "user" attribute. Try running "composer require symfony/security-bundle"'); } if (null === $token = $this->tokenStorage->getToken()) { $token = new NullToken(); } return [ 'token' => $token, 'user' => $token->getUser(), ]; } } ================================================ FILE: src/Component/src/Symfony/ExpressionLanguage/VariablesCollection.php ================================================ $iterator */ public function __construct(private iterable $iterator) { Assert::allIsInstanceOf($this->iterator, VariablesInterface::class); } public function getVariables(): array { $variables = []; foreach ($this->iterator as $variable) { $variables = array_merge($variables, $variable->getVariables()); } return $variables; } } ================================================ FILE: src/Component/src/Symfony/ExpressionLanguage/VariablesCollectionInterface.php ================================================ $value) { if (\is_array($value)) { $vars[$key] = $this->resolve($value); continue; } // Parse only vars that contain expressions if (!str_starts_with($value, '@=')) { continue; } $value = str_replace('@=', '', $value); $vars[$key] = $this->argumentParser->parseExpression($value); } return $vars; } } ================================================ FILE: src/Component/src/Symfony/ExpressionLanguage/VarsResolverInterface.php ================================================ getFormType(); $formOptions = $this->parseFormOptions($operation->getFormOptions() ?? []); if (null === $formType) { throw new \RuntimeException(sprintf('Operation "%s" has no configured form type.', $operation->getName() ?? '')); } $request = $context->get(RequestOption::class)?->request(); if ('html' === $request?->getRequestFormat()) { return $this->formFactory->create($formType, $data, $formOptions); } return $this->formFactory->createNamed('', $formType, $data, array_merge($formOptions, ['csrf_protection' => false])); } /** * @param array $formOptions * * @return array */ private function parseFormOptions(array $formOptions): array { foreach ($formOptions as $key => $value) { if (\is_array($value)) { $formOptions[$key] = $this->parseFormOptions($value); continue; } if (!\is_scalar($value)) { throw new InvalidArgumentException(sprintf('Parameter "%s" should be a scalar or an array.', $key)); } if (!is_string($value) || !str_starts_with($value, '@=')) { continue; } $value = substr($value, 2); $formOptions[$key] = $this->argumentParser->parseExpression($value); } return $formOptions; } } ================================================ FILE: src/Component/src/Symfony/Form/Factory/FormFactoryInterface.php ================================================ decorated->provide($operation, $context); $request = $context->get(RequestOption::class)?->request(); if (null === $request) { return $data; } /** @var string $format */ $format = $request->getRequestFormat(); if ( $data instanceof Response || $operation instanceof BulkOperationInterface || !($operation instanceof CreateOperationInterface || $operation instanceof UpdateOperationInterface) || 'html' !== $format || null === $operation->getFormType() ) { return $data; } $form = $this->formFactory->create($operation, $context, $data); $form->handleRequest($request); $request->attributes->set('form', $form); return $data; } } ================================================ FILE: src/Component/src/Symfony/Request/RepositoryArgumentResolver.php ================================================ attributes->all('_route_params'), $request->query->all(), $request->request->all(), ]; foreach ($allArguments as $arguments) { $matchedArguments = FunctionArgumentsFilter::filter($reflector, $arguments); if (0 === count($matchedArguments) && $this->hasOnlyOneRequiredArrayParameter($reflector)) { $arguments = $this->filterPrivateArguments($arguments); return [$arguments]; } if ('__call' === $reflector->getName()) { $arguments = $this->filterPrivateArguments($arguments); if ([] === $arguments) { continue; } return array_values($arguments); } if ([] === $matchedArguments) { continue; } return $matchedArguments; } return []; } /** * @param array $arguments */ private function filterPrivateArguments(array $arguments): array { return array_filter($arguments, function (string $key): bool { return !str_starts_with($key, '_'); }, \ARRAY_FILTER_USE_KEY); } private function hasOnlyOneRequiredArrayParameter(\ReflectionFunctionAbstract $reflector): bool { /** @var array|\ReflectionParameter[] $parameters */ $parameters = $reflector->getParameters(); $parameters = array_filter($parameters, function ($parameter): bool { return !$parameter->isDefaultValueAvailable(); }); if (1 !== \count($parameters)) { return false; } $parameterType = $parameters[0]->getType()?->__toString(); return 'array' === $parameterType; } } ================================================ FILE: src/Component/src/Symfony/Request/State/ApiResponder.php ================================================ get(RequestOption::class)?->request(); if (null === $request) { return null; } $isValid = $request->attributes->getBoolean('is_valid', true); Assert::string($data, 'Data are not serialized but it should.'); /** @var string $format */ $format = $request->getRequestFormat(); /** @var string $mimeType */ $mimeType = $request->getMimeType($format); $headers = $this->headersInitializer->initializeHeaders($mimeType); $status = Response::HTTP_OK; if ($operation instanceof CreateOperationInterface) { $status = Response::HTTP_CREATED; } if ($operation instanceof DeleteOperationInterface || $operation instanceof UpdateOperationInterface) { $status = Response::HTTP_NO_CONTENT; } return new Response($data, $isValid ? $status : Response::HTTP_UNPROCESSABLE_ENTITY, $headers); } } ================================================ FILE: src/Component/src/Symfony/Request/State/Provider.php ================================================ get(RequestOption::class)?->request(); $repository = $operation->getRepository(); if ( null === $request || null === $repository ) { return null; } $repositoryInstance = null; $arguments = $this->parseArgumentValues($operation->getRepositoryArguments() ?? []); if (\is_string($repository)) { $defaultMethod = $operation instanceof CollectionOperationInterface ? 'createPaginator' : 'findOneBy'; if ($operation instanceof BulkOperationInterface) { $defaultMethod = 'findById'; } $customMethod = $operation->getRepositoryMethod(); $method = $customMethod ?? $defaultMethod; if (!$this->locator->has($repository)) { throw new RuntimeException(sprintf('Repository "%s" not found on operation "%s".', $repository, $operation->getName() ?? '')); } /** @var object $repositoryInstance */ $repositoryInstance = $this->locator->get($repository); if ( !str_starts_with($method, 'find') && // to allow magic calls on Doctrine repository methods !\method_exists($repositoryInstance, $method)) { $errorMessage = sprintf('Method "%s" not found on repository "%s". You can either add it or configure another one in the repositoryMethod option for your operation.', $method, get_debug_type($repositoryInstance)); if ('createPaginator' === $method) { $errorMessage = sprintf('Method "%s" not found on repository "%s". You can use the "%s" trait on this repository class.', $method, get_debug_type($repositoryInstance), CreatePaginatorTrait::class); } throw new RuntimeException($errorMessage); } // make it as callable /** @var callable $repository */ $repository = [$repositoryInstance, $method]; } try { $reflector = CallableReflection::from($repository); } catch (\ReflectionException $exception) { if (null === $repositoryInstance) { throw $exception; } /** @var callable $callable */ $callable = [$repositoryInstance, '__call']; $reflector = CallableReflection::from($callable); } if ([] === $arguments) { $arguments = $this->argumentResolver->getArguments($request, $reflector); } $data = $repository(...$arguments); if ($data instanceof PagerfantaInterface) { $currentPage = $request->query->getInt('page', 1); $data->setCurrentPage($currentPage); } return $data; } private function parseArgumentValues(array $arguments): array { foreach ($arguments as $key => $value) { if (is_array($value)) { $arguments[$key] = $this->parseArgumentValues($value); continue; } if (!\is_scalar($value)) { throw new InvalidArgumentException(sprintf('Parameter "%s" should be a scalar or an array.', $key)); } $arguments[$key] = \is_string($value) ? $this->parseStringValue($value) : $value; } return $arguments; } private function parseStringValue(string $value): mixed { if (!str_starts_with($value, '@=')) { $value = '@=' . $value; trigger_deprecation('sylius/resource-bundle', '1.14', 'You passed "%s" as a string value in your repository arguments. If this is a value that needs to be parsed using the expression language, please prefix your string with "@=". In your case, use "@=%s"."', $value, $value); } // Not reachable as long as the BC layer above is there if (!str_starts_with($value, '@=')) { return $value; } $value = substr($value, 2); return $this->argumentParser->parseExpression($value); } } ================================================ FILE: src/Component/src/Symfony/Request/State/Responder.php ================================================ get(RequestOption::class)?->request(); if (null === $request) { return null; } $format = $request->getRequestFormat(); if ('html' === $format) { if (!$this->locator->has(self::RESPONDER_HTML)) { throw new \LogicException(sprintf('Responder "%s" was not found but it should.', self::RESPONDER_HTML)); } /** @var ResponderInterface $responder */ $responder = $this->locator->get(self::RESPONDER_HTML); return $responder->respond($data, $operation, $context); } if (!$this->locator->has(self::RESPONDER_API)) { throw new \LogicException(sprintf('Responder "%s" was not found but it should.', self::RESPONDER_API)); } /** @var ResponderInterface $responder */ $responder = $this->locator->get(self::RESPONDER_API); return $responder->respond($data, $operation, $context); } } ================================================ FILE: src/Component/src/Symfony/Request/State/TwigResponder.php ================================================ get(RequestOption::class)?->request(); if (null === $this->twig) { throw new \LogicException('You can not use the "Twig" if it is not available. Try running "composer require twig".'); } if (null === $request) { return null; } $isValid = $request->attributes->getBoolean('is_valid', true); if ($operation instanceof DeleteOperationInterface && $operation instanceof HttpOperation) { return $this->redirectHandler->redirectToResource($data, $operation, $request); } if ( $isValid && !$request->isMethodSafe() && $operation instanceof HttpOperation && ($operation instanceof UpdateOperationInterface || $operation instanceof CreateOperationInterface) ) { return $this->redirectHandler->redirectToResource($data, $operation, $request); } $content = $this->twig->render( $operation->getTemplate() ?? '', $this->contextFactory->create($data, $operation, $context), ); return new Response($content, $request->isMethodSafe() || $isValid ? Response::HTTP_OK : Response::HTTP_UNPROCESSABLE_ENTITY); } } ================================================ FILE: src/Component/src/Symfony/Response/ApiHeadersInitiator.php ================================================ sprintf('%s; charset=utf-8', $mimeType), 'Vary' => 'Accept', 'X-Content-Type-Options' => 'nosniff', 'X-Frame-Options' => 'deny', ]; } } ================================================ FILE: src/Component/src/Symfony/Response/HeadersInitiatorInterface.php ================================================ resourceMetadataFactory->create($className); /** @var ResourceMetadata $resource */ foreach ($resourceMetadata->getIterator() as $resource) { $this->createRoutesForResource($routeCollection, $resource); } } private function createRoutesForResource(RouteCollection $routeCollection, ResourceMetadata $resource): void { foreach ($resource->getOperations() ?? new Operations() as $operation) { if (!$operation instanceof HttpOperation) { continue; } $this->addRouteForOperation($routeCollection, $resource, $operation); } } private function addRouteForOperation(RouteCollection $routeCollection, ResourceMetadata $resource, HttpOperation $operation): void { $metadata = $this->resourceRegistry->get($resource->getAlias() ?? ''); $routeName = $operation->getRouteName(); Assert::notNull($routeName, sprintf('Operation %s has no route name. Please define one.', $operation::class)); $route = $this->createRoute($metadata, $resource, $operation); $routeCollection->add($routeName, $route, $operation->getRoutePriority() ?? 0); } private function createRoute(MetadataInterface $metadata, ResourceMetadata $resource, HttpOperation $operation): Route { return $this->operationRouteFactory->create($metadata, $resource, $operation); } } ================================================ FILE: src/Component/src/Symfony/Routing/Factory/AttributesOperationRouteFactoryInterface.php ================================================ getPath() ?? $this->getDefaultRoutePath($metadata, $resource, $operation); if (null !== $routePrefix = $operation->getRoutePrefix()) { $routePath = sprintf('%s/%s', rtrim($routePrefix, '/'), ltrim($routePath, '/')); } return new Route( path: $routePath, defaults: [ '_controller' => 'sylius.main_controller', '_sylius' => $this->getSyliusOptions($resource, $operation), ], requirements: $operation->getRouteRequirements() ?? [], methods: $operation->getMethods() ?? [], condition: $operation->getRouteCondition(), ); } private function getDefaultRoutePath(MetadataInterface $legacyMetadata, ResourceMetadata $resource, HttpOperation $operation): string { return $this->getDefaultRoutePathForOperation($legacyMetadata, $resource, $operation); } private function getDefaultRoutePathForOperation(MetadataInterface $legacyMetadata, ResourceMetadata $resource, HttpOperation $operation): string { if (null !== $path = $operation->getPath()) { return $path; } return $this->routePathFactory->createRoutePath($operation, $this->getRootPath($legacyMetadata, $resource)); } private function getRootPath(MetadataInterface $legacyMetadata, ResourceMetadata $resource): string { if ($this->routingPathBcLayer) { if (!class_exists(Urlizer::class) || !class_exists(Transliterator::class)) { throw new RuntimeException('Cannot use the routing bc-layer when the "behat/transliterator" package is not installed. Try to disable the routing path bc-layer in the Sylius Resource Bundle configuration using "sylius_resource.routing_path_bc_layer: false"'); } return Urlizer::urlize($legacyMetadata->getPluralName()); } return $this->pathSegmentNameGenerator->getSegmentName( name: $resource->getPluralName() ?? '', pluralize: false, ); } private function getSyliusOptions(ResourceMetadata $resource, HttpOperation $operation): array { $options = ['resource' => $resource->getAlias()]; if (null !== $section = $resource->getSection()) { $options['section'] = $section; } // For Legacy Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration if (null !== $vars = $operation->getVars()) { $options['vars'] = $vars; } return $options; } } ================================================ FILE: src/Component/src/Symfony/Routing/Factory/OperationRouteFactoryInterface.php ================================================ resourceMetadataFactory->create($className); /** @var ResourceMetadata $resource */ foreach ($resourceMetadata->getIterator() as $resource) { $this->createRoutesForResource($routeCollection, $resource); } return $routeCollection; } private function createRoutesForResource(RouteCollection $routeCollection, ResourceMetadata $resource): void { foreach ($resource->getOperations() ?? new Operations() as $operation) { if (!$operation instanceof HttpOperation) { continue; } $this->addRouteForOperation($routeCollection, $resource, $operation); } } private function addRouteForOperation(RouteCollection $routeCollection, ResourceMetadata $resource, HttpOperation $operation): void { $alias = $resource->getAlias(); Assert::notNull($alias, sprintf('Resource of %s has no alias.', $resource->getClass() ?? '')); $metadata = $this->resourceRegistry->get($alias); $routeName = $operation->getRouteName(); Assert::notNull($routeName, sprintf( 'Operation %s of %s has no route name. Please define one.', $operation::class, $alias, )); $route = $this->createRoute($metadata, $resource, $operation); $routeCollection->add($routeName, $route, $operation->getRoutePriority() ?? 0); } private function createRoute(MetadataInterface $metadata, ResourceMetadata $resource, HttpOperation $operation): Route { return $this->operationRouteFactory->create($metadata, $resource, $operation); } } ================================================ FILE: src/Component/src/Symfony/Routing/Factory/Resource/ResourceRouteCollectionFactoryInterface.php ================================================ getResource(); if (null === $resource) { throw new \RuntimeException(sprintf('No resource was found on the operation "%s"', $operation->getShortName() ?? '')); } $section = $resource->getSection(); $sectionPrefix = $section ? $section . '_' : ''; return sprintf( '%s_%s%s_%s', $resource->getApplicationName() ?? '', $sectionPrefix, $resource->getName() ?? '', $shortName ?? $operation->getShortName() ?? '', ); } } ================================================ FILE: src/Component/src/Symfony/Routing/Factory/RouteName/OperationRouteNameFactoryInterface.php ================================================ getShortName() ?? ''; if ($operation instanceof BulkOperationInterface) { return sprintf('%s/%s', $rootPath, $this->pathSegmentNameGenerator->getSegmentName($shortName, false)); } return $this->decorated->createRoutePath($operation, $rootPath); } } ================================================ FILE: src/Component/src/Symfony/Routing/Factory/RoutePath/CollectionOperationRoutePathFactory.php ================================================ getShortName(); if ($operation instanceof CollectionOperationInterface) { $path = match ($shortName) { 'index', 'get_collection' => '', default => '/' . $this->pathSegmentNameGenerator->getSegmentName($shortName ?? '', false), }; return sprintf('%s%s', $rootPath, $path); } return $this->decorated->createRoutePath($operation, $rootPath); } } ================================================ FILE: src/Component/src/Symfony/Routing/Factory/RoutePath/CreateOperationRoutePathFactory.php ================================================ getShortName(); if ($operation instanceof CreateOperationInterface) { $path = match ($shortName) { 'create' => '/new', 'post' => '', default => '/' . $this->pathSegmentNameGenerator->getSegmentName($shortName ?? '', false), }; return sprintf('%s%s', $rootPath, $path); } return $this->decorated->createRoutePath($operation, $rootPath); } } ================================================ FILE: src/Component/src/Symfony/Routing/Factory/RoutePath/DeleteOperationRoutePathFactory.php ================================================ getShortName(); $identifier = $operation->getResource()?->getIdentifier() ?? 'id'; if ($operation instanceof DeleteOperationInterface) { $path = $operation instanceof ApiOperationInterface && 'delete' === $shortName ? '' : '/' . $this->pathSegmentNameGenerator->getSegmentName($shortName ?? '', false) ; return sprintf('%s/{%s}%s', $rootPath, $identifier, $path); } return $this->decorated->createRoutePath($operation, $rootPath); } } ================================================ FILE: src/Component/src/Symfony/Routing/Factory/RoutePath/OperationRoutePathFactory.php ================================================ getName() ?? '')); } } ================================================ FILE: src/Component/src/Symfony/Routing/Factory/RoutePath/OperationRoutePathFactoryInterface.php ================================================ getShortName(); $identifier = $operation->getResource()?->getIdentifier() ?? 'id'; if ($operation instanceof ShowOperationInterface) { $path = match ($shortName) { 'show', 'get' => '', default => '/' . $this->pathSegmentNameGenerator->getSegmentName($shortName ?? '', false), }; return sprintf('%s/{%s}%s', $rootPath, $identifier, $path); } return $this->decorated->createRoutePath($operation, $rootPath); } } ================================================ FILE: src/Component/src/Symfony/Routing/Factory/RoutePath/UpdateOperationRoutePathFactory.php ================================================ getShortName(); $identifier = $operation->getResource()?->getIdentifier() ?? 'id'; if ($operation instanceof UpdateOperationInterface) { $path = match ($shortName) { 'update' => '/edit', 'put', 'patch' => '', default => '/' . $this->pathSegmentNameGenerator->getSegmentName($shortName ?? '', false), }; return sprintf('%s/{%s}%s', $rootPath, $identifier, $path); } return $this->decorated->createRoutePath($operation, $rootPath); } } ================================================ FILE: src/Component/src/Symfony/Routing/Loader/ResourceLoader.php ================================================ resourceClassListFactory->create(); /** @var class-string $class */ foreach ($resourceClasses as $class) { $routeCollection->addCollection($this->resourceRouteCollectionFactory->createRouteCollectionForClass($class)); } return $routeCollection; } } ================================================ FILE: src/Component/src/Symfony/Routing/RedirectHandler.php ================================================ getRedirectTo()) { /** @var string|null $referer */ $referer = $request->headers->get('referer'); if (null !== $referer && $this->isValidReferer($referer, $request)) { return new RedirectResponse($referer); } } $route = $operation->getRedirectToRoute(); if (null === $route) { throw new \RuntimeException(sprintf('Operation "%s" has no redirection route, but it should.', $operation->getName() ?? '')); } $parameters = $this->getRouteArguments($data, $operation); return $this->redirectToRoute($data, $route, $parameters); } public function redirectToOperation(mixed $data, HttpOperation $operation, Request $request, string $newOperation): RedirectResponse { $route = $this->operationRouteNameFactory->createRouteName($operation, $newOperation); $parameters = $this->getRouteArguments($data, $operation); return $this->redirectToRoute($data, $route, $parameters); } public function redirectToRoute(mixed $data, string $route, array $parameters = []): RedirectResponse { if (\str_ends_with($route, '_index') && [] === $parameters) { $parameters = $this->filterStorage?->all() ?? []; } return new RedirectResponse($this->router->generate($route, $parameters)); } private function getRouteArguments(mixed $data, HttpOperation $operation): array { $resource = $operation->getResource(); if (null === $resource) { throw new \RuntimeException(sprintf('Operation "%s" has no resource, but it should.', $operation->getName() ?? '')); } $redirectArguments = $operation->getRedirectArguments() ?? []; if ( [] === $redirectArguments && !$operation instanceof DeleteOperationInterface && !$operation instanceof BulkOperationInterface ) { $identifier = $resource->getIdentifier() ?? 'id'; $redirectArguments[$identifier] = 'resource.' . $identifier; } return $this->parseResourceValues($resource, $redirectArguments, $data); } private function parseResourceValues(ResourceMetadata $resource, array $parameters, mixed $data): array { $accessor = PropertyAccess::createPropertyAccessor(); foreach ($parameters as $key => $value) { if (\is_array($value)) { $parameters[$key] = $this->parseResourceValues($resource, $value, $data); continue; } if (!\is_scalar($value)) { throw new InvalidArgumentException(sprintf('Parameter "%s" should be a scalar or an array.', $key)); } if (\is_string($value) && str_starts_with($value, 'resource.')) { $propertyPath = substr($value, 9); if (\is_object($data) && $accessor->isReadable($data, $propertyPath)) { $parameters[$key] = $accessor->getValue($data, $propertyPath); continue; } } $variables = ['resource' => $data]; $resourceName = $resource->getName(); if (null !== $resourceName) { $variables[$resourceName] = $data; } $parameters[$key] = \is_string($value) ? $this->parseStringValue($value, $variables) : $value; } return $parameters; } /** * @param array $variables */ private function parseStringValue(string $value, array $variables): mixed { if (!str_starts_with($value, '@=')) { $value = '@=' . $value; trigger_deprecation('sylius/resource-bundle', '1.14', 'You passed "%s" as a string value in your redirect arguments. If this is a value that needs to be parsed using the expression language, please prefix your string with "@=". In your case, use "@=%s"."', $value, $value); } // Not reachable as long as the BC layer above is there if (!str_starts_with($value, '@=')) { return $value; } $value = substr($value, 2); return $this->argumentParser->parseExpression($value, $variables); } private function isValidReferer(string $referer, Request $request): bool { $parsed = parse_url($referer); if ($parsed === false) { return false; } // Relative URL → OK if (!isset($parsed['host'])) { return true; } // Same host only return $parsed['host'] === $request->getHost(); } } ================================================ FILE: src/Component/src/Symfony/Routing/RedirectHandlerInterface.php ================================================ tokenStorage || null === $this->authenticationTrustResolver) { throw new \LogicException('The "symfony/security" library must be installed to use the "security" attribute.'); } if (null === $this->expressionLanguage) { throw new \LogicException('The "symfony/expression-language" library must be installed to use the "security" attribute.'); } $expression = $operation->getSecurity(); if (null === $expression) { return true; } $token = $this->tokenStorage->getToken(); if (null === $token) { $token = new NullToken(); } $variables = array_merge($extraVariables, $this->getVariables($token)); return (bool) $this->expressionLanguage->evaluate($expression, $variables); } /** * @see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php */ private function getVariables(TokenInterface $token): array { $roleNames = $token->getRoleNames(); if (null !== $this->roleHierarchy) { $roleNames = $this->roleHierarchy->getReachableRoleNames($roleNames); } return [ 'token' => $token, 'user' => $token->getUser(), 'roles' => $roleNames, 'trust_resolver' => $this->authenticationTrustResolver, 'auth_checker' => $this->authorizationChecker, // needed for the is_granted expression function ]; } } ================================================ FILE: src/Component/src/Symfony/Serializer/State/DeserializeProvider.php ================================================ decorated->provide($operation, $context); if (!$operation instanceof HttpOperation) { return $data; } $request = $context->get(RequestOption::class)?->request() ?? null; if (!$request) { return $data; } if (!($operation->canDeserialize() ?? true)) { return $data; } $resourceClass = $operation->getResource()?->getClass(); /** @var string $format */ $format = $request->getRequestFormat(); if ( null === $resourceClass || 'html' === $format || $request->isMethodSafe() || $operation instanceof DeleteOperationInterface ) { return $data; } if (null === $this->serializer) { throw new \LogicException(sprintf('You can not use the "%s" format if the Serializer is not available. Try running "composer require symfony/serializer".', $format)); } $denormalizationContext = $operation->getDenormalizationContext() ?? []; $method = $request->getMethod(); if (null !== $data && in_array($method, ['POST', 'PATCH', 'PUT'])) { $denormalizationContext[AbstractNormalizer::OBJECT_TO_POPULATE] = $data; } return $this->serializer->deserialize($request->getContent(), $resourceClass, $format, $denormalizationContext); } } ================================================ FILE: src/Component/src/Symfony/Serializer/State/SerializeProcessor.php ================================================ get(RequestOption::class)?->request(); if (null === $request) { return $this->processor->process($data, $operation, $context); } /** @var string $format */ $format = $request->getRequestFormat(); if ( 'html' === $format || !($operation->canSerialize() ?? true) ) { return $this->processor->process($data, $operation, $context); } if (null === $this->serializer) { throw new \LogicException(sprintf('You can not use the "%s" format if the Serializer is not available. Try running "composer require symfony/serializer".', $format)); } $serialized = $this->serializer->serialize($data, $format, $operation->getNormalizationContext() ?? []); return $this->processor->process($serialized, $operation, $context); } } ================================================ FILE: src/Component/src/Symfony/Session/Flash/FlashHelper.php ================================================ addFlashFromOperation($operation, $context, 'success', $message); } public function addErrorFlash(Operation $operation, Context $context, ?string $message = null): void { $this->addFlashFromOperation($operation, $context, 'error', $message); } public function addFlashFromEvent(GenericEvent $event, Context $context): void { $message = $this->buildEventMessage($event); $this->addFlash($message, $event->getMessageType(), $context); } private function addFlashFromOperation(Operation $operation, Context $context, string $type, ?string $message): void { $message ??= $this->buildOperationMessage($operation, $type); $this->addFlash($message, $type, $context); } private function buildEventMessage(GenericEvent $event): string { $message = $event->getMessage(); $parameters = $event->getMessageParameters(); if (!$this->translator instanceof TranslatorBagInterface) { return $this->translator->trans($message, $parameters, 'flashes'); } if ($this->translator->getCatalogue()->has($message, 'flashes')) { return $this->translator->trans($message, $parameters, 'flashes'); } return $message; } private function buildOperationMessage(Operation $operation, string $type): string { $resource = $operation->getResource(); Assert::notNull($resource); $translationKeys = iterator_to_array($this->getTranslationKeys($resource, $operation, $type)); /** @var string $firstTranslationKey */ $firstTranslationKey = reset($translationKeys); $parameters = $this->getTranslationParameters($operation); $notificationMessage = $operation->getNotificationMessage(); // It's defined by the user, it should be used. if ('success' === $type && null !== $notificationMessage) { // Do not use the translator if not needed if ($this->translator instanceof TranslatorBagInterface && !$this->translator->getCatalogue()->has($notificationMessage, 'flashes')) { return $notificationMessage; } return $this->translator->trans($notificationMessage, $parameters, 'flashes'); } if (!$this->translator instanceof TranslatorBagInterface) { return $this->translator->trans($firstTranslationKey, $parameters, 'flashes'); } foreach ($translationKeys as $translationKey) { if ($this->translator->getCatalogue()->has($translationKey, 'flashes')) { return $this->translator->trans($translationKey, $parameters, 'flashes'); } } // Last fallback, use the first translation key. return $this->translator->trans($firstTranslationKey, $parameters, 'flashes'); } private function addFlash(string $message, string $type, Context $context): void { $request = $context->get(RequestOption::class)?->request(); if (null === $request) { return; } /** @var FlashBagInterface $flashBag */ $flashBag = $request->getSession()->getBag('flashes'); $flashBag->add($type, $message); } private function getTranslationParameters(Operation $operation): array { $resource = $operation->getResource(); if (null === $resource) { return []; } $resourceName = $this->translateResource($resource); $humanizedName = $resourceName ?? ucfirst(StringHumanizer::humanize($resource->getName() ?? '')); if ($operation instanceof BulkOperationInterface) { $resourcePluralName = $this->translateResource($resource, true); $humanizedPluralName = $resourcePluralName ?? ucfirst(StringHumanizer::humanize($resource->getPluralName() ?? '')); return [ '%resource%' => $humanizedName, '%resources%' => $humanizedPluralName, ]; } return ['%resource%' => $humanizedName]; } private function translateResource(ResourceMetadata $resource, bool $plurialize = false): ?string { $translationKey = sprintf( '%s.ui.%s', $resource->getApplicationName() ?? '', $plurialize ? ($resource->getPluralName() ?? '') : ($resource->getName() ?? ''), ); if ($this->translator instanceof TranslatorBagInterface && $this->translator->getCatalogue()->has($translationKey)) { return $this->translator->trans($translationKey); } return null; } /** * @return iterable */ private function getTranslationKeys(ResourceMetadata $resource, Operation $operation, string $type): iterable { $applicationName = $resource->getApplicationName() ?? ''; $resourceName = $resource->getName() ?? ''; $operationShortName = $operation->getShortName() ?? ''; $translationKeySuffix = 'error' === $type ? '_error' : ''; /** * Examples: * app.product.my_operation * app.product.my_operation_error */ yield sprintf( '%s.%s.%s%s', $applicationName, $resourceName, $operationShortName, $translationKeySuffix, ); $genericOperationType = $this->getGenericOperationType($operation); /** * Examples: * app.product.delete * app.product.delete_error */ if ($genericOperationType !== $operationShortName) { yield sprintf( '%s.%s.%s%s', $applicationName, $resourceName, $genericOperationType, $translationKeySuffix, ); } /** * Examples: * sylius.resource.my_operation * sylius.resource.my_operation_error */ yield sprintf( 'sylius.resource.%s%s', $operationShortName, $translationKeySuffix, ); /** * Examples: * sylius.resource.delete * sylius.resource.delete_error */ if ($genericOperationType !== $operationShortName) { yield sprintf( 'sylius.resource.%s%s', $genericOperationType, $translationKeySuffix, ); } } private function getGenericOperationType(Operation $operation): ?string { if ($operation instanceof DeleteOperationInterface) { return 'delete'; } if ($operation instanceof CreateOperationInterface) { return 'create'; } if ($operation instanceof UpdateOperationInterface) { return 'update'; } return null; } } ================================================ FILE: src/Component/src/Symfony/Session/Flash/FlashHelperInterface.php ================================================ getThrowable(); if (!$exception instanceof ConstraintViolationListAwareExceptionInterface) { return; } if (null === $this->serializer) { throw new \LogicException('The Symfony Serializer is not available. Try running "composer require symfony/serializer".'); } $request = $event->getRequest(); /** @var string $format */ $format = $request->getRequestFormat(); /** @var string $mimeType */ $mimeType = $request->getMimeType($format); $event->setResponse(new Response( $this->serializer->serialize($exception->getConstraintViolationList(), $format), Response::HTTP_UNPROCESSABLE_ENTITY, [ 'Content-Type' => sprintf('%s; charset=utf-8', $mimeType), 'X-Content-Type-Options' => 'nosniff', 'X-Frame-Options' => 'deny', ], )); } } ================================================ FILE: src/Component/src/Symfony/Validator/Exception/ConstraintViolationListAwareExceptionInterface.php ================================================ __toString(), $code, $previous); } public function getConstraintViolationList(): ConstraintViolationListInterface { return $this->constraintViolationList; } public function __toString(): string { $message = ''; foreach ($this->constraintViolationList as $violation) { if ('' !== $message) { $message .= "\n"; } if ($propertyPath = $violation->getPropertyPath()) { $message .= "$propertyPath: "; } $message .= $violation->getMessage(); } return $message; } } ================================================ FILE: src/Component/src/Symfony/Validator/State/ValidateProvider.php ================================================ decorated->provide($operation, $context); $request = $context->get(RequestOption::class)?->request(); /** @var FormInterface|null $form */ $form = $request?->attributes->get('form'); /** @var string $format */ $format = $request?->getRequestFormat(); if ( $data instanceof Response || !($operation instanceof CreateOperationInterface || $operation instanceof UpdateOperationInterface) || !($operation->canValidate() ?? true) ) { return $data; } if ('html' !== $format) { $validationGroups = $operation->getValidationContext()['groups'] ?? null; $violations = $this->validator->validate(value: $data, groups: $validationGroups); if (0 !== \count($violations)) { throw new ValidationException($violations); } return $data; } if (null === $form || null === $request) { return $data; } if ( !$request->isMethodSafe() && $form->isSubmitted() && $form->isValid() ) { $request->attributes->set('is_valid', true); /** @var array|object|null $data */ $data = $form->getData(); return $data; } $request->attributes->set('is_valid', false); return $data; } } ================================================ FILE: src/Component/src/Symfony/Workflow/OperationStateMachine.php ================================================ getStateMachineTransition() ?? null; Assert::notNull($transition, sprintf('No State machine transition was found on operation "%s".', $operation->getName() ?? '')); $graph = $operation->getStateMachineGraph() ?? null; return $this->getRegistry()->get($data, $graph)->can($data, $transition); } public function apply(object $data, Operation $operation, Context $context): void { Assert::isInstanceOf($operation, StateMachineAwareOperationInterface::class); $transition = $operation->getStateMachineTransition() ?? null; Assert::notNull($transition, sprintf('No State machine transition was found on operation "%s".', $operation->getName() ?? '')); $graph = $operation->getStateMachineGraph() ?? null; $this->getRegistry()->get($data, $graph)->apply($data, $transition); } private function getRegistry(): Registry { if (null === $this->registry) { throw new \LogicException('You can not use the "state-machine" if Symfony workflow is not available. Try running "composer require symfony/workflow".'); } return $this->registry; } } ================================================ FILE: src/Component/src/Translation/Provider/ImmutableTranslationLocaleProvider.php ================================================ definedLocalesCodes; } public function getDefaultLocaleCode(): string { return $this->defaultLocaleCode; } } if (!class_exists(\Sylius\Component\Resource\Translation\Provider\ImmutableTranslationLocaleProvider::class, false)) { class_alias(ImmutableTranslationLocaleProvider::class, \Sylius\Component\Resource\Translation\Provider\ImmutableTranslationLocaleProvider::class); } ================================================ FILE: src/Component/src/Translation/Provider/TranslationLocaleProviderInterface.php ================================================ translationLocaleProvider->getDefaultLocaleCode(); $translatableEntity->setCurrentLocale($localeCode); $translatableEntity->setFallbackLocale($localeCode); } } if (!class_exists(\Sylius\Component\Resource\Translation\TranslatableEntityLocaleAssigner::class, false)) { class_alias(TranslatableEntityLocaleAssigner::class, \Sylius\Component\Resource\Translation\TranslatableEntityLocaleAssigner::class); } ================================================ FILE: src/Component/src/Translation/TranslatableEntityLocaleAssignerInterface.php ================================================ getTwigContextFactory() ) { return []; } if (\is_callable($twigContextFactory)) { return $twigContextFactory($data, $operation, $context); } if (!$this->locator->has($twigContextFactory)) { throw new \RuntimeException(sprintf('Twig context factory "%s" not found on operation "%s"', $twigContextFactory, $operation->getName() ?? '')); } /** @var ContextFactoryInterface $twigContextFactoryInstance */ $twigContextFactoryInstance = $this->locator->get($twigContextFactory); Assert::isInstanceOf($twigContextFactoryInstance, ContextFactoryInterface::class); return $twigContextFactoryInstance->create($data, $operation, $context); } } ================================================ FILE: src/Component/src/Twig/Context/Factory/ContextFactoryInterface.php ================================================ $operation, 'resource_metadata' => $operation->getResource(), ]; if ($operation instanceof CollectionOperationInterface) { $twigContext['resources'] = $data; $pluralName = $operation->getResource()?->getPluralName(); if (null !== $pluralName) { $twigContext[$pluralName] = $data; } } else { $twigContext['resource'] = $data; $name = $operation->getResource()?->getName(); if (null !== $name) { $twigContext[$name] = $data; } } return $twigContext; } } ================================================ FILE: src/Component/src/Twig/Context/Factory/RequestContextFactory.php ================================================ decorated->create($data, $operation, $context); $request = $context->get(RequestOption::class)?->request(); if (null === $request) { return $twigContext; } /** @var FormInterface|null $form */ $form = $request->attributes->get('form'); if (null === $form) { return $twigContext; } return array_merge($twigContext, ['form' => $form->createView()]); } } ================================================ FILE: src/Component/src/Winzou/StateMachine/OperationStateMachine.php ================================================ getStateMachineTransition() ?? null; Assert::notNull($transition, sprintf('No State machine transition was found on operation "%s".', $operation->getName() ?? '')); $graph = $operation->getStateMachineGraph() ?? 'default'; return $this->getFactory()->get($data, $graph)->can($transition); } public function apply(object $data, Operation $operation, Context $context): void { Assert::isInstanceOf($operation, StateMachineAwareOperationInterface::class); $transition = $operation->getStateMachineTransition() ?? null; Assert::notNull($transition, sprintf('No State machine transition was found on operation "%s".', $operation->getName() ?? '')); $graph = $operation->getStateMachineGraph() ?? 'default'; $this->getFactory()->get($data, $graph)->apply($transition); } private function getFactory(): Factory { if (null === $this->factory) { throw new \LogicException('You can not use the "state-machine" if Winzou State Machine is not available. Try running "composer require winzou/state-machine-bundle".'); } return $this->factory; } } ================================================ FILE: src/Component/tests/Context/ContextTest.php ================================================ assertInstanceOf(\IteratorAggregate::class, $context); } /** @test */ public function it_can_be_constructed_with_options(): void { $optionOne = new DummyClassOne(); $optionTwo = new DummyClassTwo(); $context = new Context($optionOne, $optionTwo); $this->assertEquals($optionOne, $context->get(DummyClassOne::class)); $this->assertEquals($optionTwo, $context->get(DummyClassTwo::class)); } /** @test */ public function it_can_be_with_options(): void { $optionOne = new DummyClassOne(); $optionTwo = new DummyClassTwo(); $context = new Context(); $context = $context->with($optionOne, $optionTwo); $this->assertEquals($optionOne, $context->get(DummyClassOne::class)); $this->assertEquals($optionTwo, $context->get(DummyClassTwo::class)); } /** @test */ public function it_can_be_without_options(): void { $optionOne = new DummyClassOne(); $optionTwo = new DummyClassTwo(); $context = new Context(); $context = $context->with($optionOne, $optionTwo); $context = $context->without(DummyClassOne::class, DummyClassTwo::class); $this->assertNull($context->get(DummyClassOne::class)); $this->assertNull($context->get(DummyClassTwo::class)); } /** @test */ public function it_can_be_iterated(): void { $optionOne = new DummyClassOne(); $optionTwo = new DummyClassTwo(); $context = new Context($optionOne, $optionTwo); $this->assertEquals($optionOne, $context->getIterator()->current()); $this->assertEquals(2, iterator_count($context->getIterator())); } } ================================================ FILE: src/Component/tests/Context/Initiator/RequestContextInitiatorTest.php ================================================ requestContextInitiator = new RequestContextInitiator(); } /** @test */ public function it_initializes_context(): void { $request = $this->createMock(Request::class); $request->attributes = new ParameterBag(['_sylius' => ['resource_class' => 'App\Resource']]); $context = $this->requestContextInitiator->initializeContext($request); $this->assertInstanceOf(Context::class, $context); $this->assertEquals($request, $context->get(RequestOption::class)?->request()); } } ================================================ FILE: src/Component/tests/Context/Option/MetadataOptionTest.php ================================================ metadata = $this->createMock(MetadataInterface::class); $this->metadataOption = new MetadataOption($this->metadata); } /** @test */ public function it_returns_request_configuration(): void { $this->assertEquals($this->metadata, $this->metadataOption->metadata()); } } ================================================ FILE: src/Component/tests/Context/Option/RequestOptionTest.php ================================================ request = $this->createMock(Request::class); $this->requestOption = new RequestOption($this->request); } /** @test */ public function it_contains_request(): void { $this->assertEquals($this->request, $this->requestOption->request()); } } ================================================ FILE: src/Component/tests/Doctrine/Common/Metadata/Resource/Factory/DoctrineResourceMetadataCollectionFactoryTest.php ================================================ resourceRegistry = $this->createMock(RegistryInterface::class); $this->decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $this->factory = new DoctrineResourceMetadataCollectionFactory( $this->resourceRegistry, $this->decorated, ); } public function testItIsInitializable(): void { $this->assertInstanceOf(DoctrineResourceMetadataCollectionFactory::class, $this->factory); } public function testItAddsPersistProcessorToOperationsForResourceWithDoctrineOrmDriver(): void { $metadata = $this->createMock(MetadataInterface::class); $operation = new Create(name: 'app_dummy_create'); $resource = (new ResourceMetadata(alias: 'app.dummy')) ->withOperations(new Operations([$operation])); $resourceMetadataCollection = new ResourceMetadataCollection([$resource]); $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $this->resourceRegistry->method('get')->with('app.dummy')->willReturn($metadata); $metadata->method('getDriver')->willReturn('doctrine/orm'); $result = $this->factory->create('App\Resource'); $this->assertEquals( PersistProcessor::class, $result->getOperation('app.dummy', 'app_dummy_create')->getProcessor(), ); } public function testItAddsPersistProcessorToOperationsForResourceWithDoctrineDbalDriver(): void { $metadata = $this->createMock(MetadataInterface::class); $operation = new Create(name: 'app_dummy_create'); $resource = (new ResourceMetadata(alias: 'app.dummy')) ->withOperations(new Operations([$operation])); $resourceMetadataCollection = new ResourceMetadataCollection([$resource]); $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $this->resourceRegistry->method('get')->with('app.dummy')->willReturn($metadata); $metadata->method('getDriver')->willReturn('doctrine/dbal'); $result = $this->factory->create('App\Resource'); $this->assertEquals( PersistProcessor::class, $result->getOperation('app.dummy', 'app_dummy_create')->getProcessor(), ); } public function testItAddsRemoveProcessorToDeleteOperationsForResourceWithDoctrineDriver(): void { $metadata = $this->createMock(MetadataInterface::class); $operation = new Delete(name: 'app_dummy_delete'); $resource = (new ResourceMetadata(alias: 'app.dummy')) ->withOperations(new Operations([$operation])); $resourceMetadataCollection = new ResourceMetadataCollection([$resource]); $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $this->resourceRegistry->method('get')->with('app.dummy')->willReturn($metadata); $metadata->method('getDriver')->willReturn('doctrine/orm'); $result = $this->factory->create('App\Resource'); $this->assertEquals( RemoveProcessor::class, $result->getOperation('app.dummy', 'app_dummy_delete')->getProcessor(), ); } } ================================================ FILE: src/Component/tests/Doctrine/Common/State/PersistProcessorTest.php ================================================ managerRegistry = $this->createMock(ManagerRegistry::class); $this->persistProcessor = new PersistProcessor($this->managerRegistry); } public function testItIsInitializable(): void { $this->assertInstanceOf(PersistProcessor::class, $this->persistProcessor); } public function testItPersistsDataWhenManagerDoesNotContainTheResourceYet(): void { $manager = $this->createMock(ObjectManager::class); $operation = $this->createMock(Operation::class); $data = new \stdClass(); $this->managerRegistry->method('getManagerForClass')->with(\stdClass::class)->willReturn($manager); $manager->method('contains')->with($data)->willReturn(false); $manager->expects($this->once())->method('persist')->with($data); $manager->expects($this->once())->method('flush'); $manager->expects($this->once())->method('refresh')->with($data); $this->persistProcessor->process($data, $operation, new Context()); } public function testItOnlyFlushesWhenManagerContainsTheResource(): void { $manager = $this->createMock(ObjectManager::class); $operation = $this->createMock(Operation::class); $metadata = $this->createMock(ClassMetadata::class); $data = new \stdClass(); $metadata->expects(self::once())->method('isChangeTrackingDeferredExplicit')->willReturn(false); $this->managerRegistry->method('getManagerForClass')->with(\stdClass::class)->willReturn($manager); $manager->method('contains')->with($data)->willReturn(true); $manager->method('getClassMetadata')->with(\stdClass::class)->willReturn($metadata); $manager->expects($this->never())->method('persist')->with($data); $manager->expects($this->once())->method('flush'); $manager->expects($this->once())->method('refresh')->with($data); $this->persistProcessor->process($data, $operation, new Context()); } public function testItPersistsWhenDeferredExplicitly(): void { $manager = $this->createMock(ObjectManager::class); $operation = $this->createMock(Operation::class); $classMetadataInfo = $this->createMock(ClassMetadata::class); $data = new \stdClass(); $this->managerRegistry->method('getManagerForClass')->with(\stdClass::class)->willReturn($manager); $manager->method('contains')->with($data)->willReturn(true); $manager->method('getClassMetadata')->with(\stdClass::class)->willReturn($classMetadataInfo); $classMetadataInfo->expects($this->once())->method('isChangeTrackingDeferredExplicit')->willReturn(true); $manager->expects($this->once())->method('persist')->with($data); $manager->expects($this->once())->method('flush'); $manager->expects($this->once())->method('refresh')->with($data); $this->persistProcessor->process($data, $operation, new Context()); } public function testItDoesNothingWhenDataIsNotManagedByDoctrine(): void { $operation = $this->createMock(Operation::class); $data = new \stdClass(); $this->managerRegistry->method('getManagerForClass')->with(\stdClass::class)->willReturn(null); $this->assertSame($data, $this->persistProcessor->process($data, $operation, new Context())); } public function testItDoesNothingWhenDataIsNotAnObject(): void { $operation = $this->createMock(Operation::class); $this->assertSame(1, $this->persistProcessor->process(1, $operation, new Context())); } } ================================================ FILE: src/Component/tests/Doctrine/Common/State/RemoveProcessorTest.php ================================================ managerRegistry = $this->createMock(ManagerRegistry::class); $this->removeProcessor = new RemoveProcessor($this->managerRegistry); } public function testItIsInitializable(): void { $this->assertInstanceOf(RemoveProcessor::class, $this->removeProcessor); } public function testItRemovesData(): void { $data = new \stdClass(); $operation = $this->createMock(Operation::class); $manager = $this->createMock(ObjectManager::class); $this->managerRegistry->method('getManagerForClass')->with(\stdClass::class)->willReturn($manager); $manager->method('contains')->with($data)->willReturn(false); $manager->expects($this->once())->method('remove')->with($data); $manager->expects($this->once())->method('flush'); $result = $this->removeProcessor->process($data, $operation, new Context()); $this->assertSame($data, $result); } public function testItDoesNothingWhenDataIsNotManagedByDoctrine(): void { $data = new \stdClass(); $operation = $this->createMock(Operation::class); $this->managerRegistry->method('getManagerForClass')->with(\stdClass::class)->willReturn(null); $this->assertNull($this->removeProcessor->process($data, $operation, new Context())); } public function testItDoesNothingWhenDataIsNotAnObject(): void { $operation = $this->createMock(Operation::class); $this->assertNull($this->removeProcessor->process(1, $operation, new Context())); } } ================================================ FILE: src/Component/tests/Doctrine/Persistence/Exception/ResourceExistsExceptionTest.php ================================================ assertInstanceOf(\Exception::class, $exception); } public function testItExtendsRuntimeException(): void { $exception = new ResourceExistsException(); $this->assertInstanceOf(\RuntimeException::class, $exception); } public function testItImplementsExceptionInterface(): void { $exception = new ResourceExistsException(); $this->assertInstanceOf(ExceptionInterface::class, $exception); } public function testItHasAMessage(): void { $exception = new ResourceExistsException(); $this->assertEquals('Given resource already exists in the repository.', $exception->getMessage()); } } ================================================ FILE: src/Component/tests/Doctrine/Persistence/InMemoryRepositoryTest.php ================================================ repository = new InMemoryRepository(SampleBookResourceInterface::class); } public function testItThrowsUnexpectedTypeExceptionWhenConstructingWithoutResourceInterface(): void { $this->expectException(UnexpectedTypeException::class); new InMemoryRepository(\stdClass::class); } public function testItImplementsRepositoryInterface(): void { $this->assertInstanceOf(RepositoryInterface::class, $this->repository); } public function testItThrowsInvalidArgumentExceptionWhenAddingWrongResourceType(): void { /** @var MockObject $resource */ $resource = $this->createMock(ResourceInterface::class); $this->expectException(\InvalidArgumentException::class); $this->repository->add($resource); } public function testItAddsAnObject(): void { /** @var MockObject $monocle */ $monocle = $this->createMock(SampleBookResourceInterface::class); $monocle->method('getId')->willReturn(2); $this->repository->add($monocle); $this->assertSame($monocle, $this->repository->findOneBy(['id' => 2])); } public function testItThrowsExistingResourceExceptionOnAddingAResourceWhichIsAlreadyInRepository(): void { /** @var MockObject $bike */ $bike = $this->createMock(SampleBookResourceInterface::class); $this->repository->add($bike); $this->expectException(ResourceExistsException::class); $this->repository->add($bike); } public function testItRemovesAResource(): void { /** @var MockObject $shirt */ $shirt = $this->createMock(SampleBookResourceInterface::class); $shirt->method('getId')->willReturn(5); $this->repository->add($shirt); $this->repository->remove($shirt); $this->assertNull($this->repository->findOneBy(['id' => 5])); } public function testItFindsObjectById(): void { /** @var MockObject $monocle */ $monocle = $this->createMock(SampleBookResourceInterface::class); $monocle->method('getId')->willReturn(2); $this->repository->add($monocle); $this->assertSame($monocle, $this->repository->find(2)); } public function testItReturnsNullIfCannotFindObjectById(): void { $this->assertNull($this->repository->find(2)); } public function testItReturnsAllObjectsWhenFindingByAnEmptyParameterArray(): void { /** @var MockObject $book */ $book = $this->createMock(SampleBookResourceInterface::class); /** @var MockObject $shirt */ $shirt = $this->createMock(SampleBookResourceInterface::class); $book->method('getId')->willReturn(10); $book->method('getName')->willReturn('Book'); $shirt->method('getId')->willReturn(5); $shirt->method('getName')->willReturn('Shirt'); $this->repository->add($book); $this->repository->add($shirt); $this->assertSame([$book, $shirt], $this->repository->findBy([])); } public function testItFindsManyObjectsByMultipleCriteriaOrdersALimitAndAnOffset(): void { /** @var MockObject $firstBook */ $firstBook = $this->createMock(SampleBookResourceInterface::class); /** @var MockObject $secondBook */ $secondBook = $this->createMock(SampleBookResourceInterface::class); /** @var MockObject $thirdBook */ $thirdBook = $this->createMock(SampleBookResourceInterface::class); /** @var MockObject $fourthBook */ $fourthBook = $this->createMock(SampleBookResourceInterface::class); /** @var MockObject $wrongIdBook */ $wrongIdBook = $this->createMock(SampleBookResourceInterface::class); /** @var MockObject $wrongNameBook */ $wrongNameBook = $this->createMock(SampleBookResourceInterface::class); $id = 80; $name = 'Book'; $firstBook->method('getId')->willReturn($id); $secondBook->method('getId')->willReturn($id); $thirdBook->method('getId')->willReturn($id); $fourthBook->method('getId')->willReturn($id); $wrongNameBook->method('getId')->willReturn($id); $wrongIdBook->method('getId')->willReturn(100); $firstBook->method('getName')->willReturn($name); $secondBook->method('getName')->willReturn($name); $thirdBook->method('getName')->willReturn($name); $fourthBook->method('getName')->willReturn($name); $wrongIdBook->method('getName')->willReturn($name); $wrongNameBook->method('getName')->willReturn('Tome'); $firstBook->method('getRating')->willReturn(3); $secondBook->method('getRating')->willReturn(2); $thirdBook->method('getRating')->willReturn(2); $fourthBook->method('getRating')->willReturn(4); $firstBook->method('getTitle')->willReturn('World War Z'); $secondBook->method('getTitle')->willReturn('World War Z'); $thirdBook->method('getTitle')->willReturn('Call of Cthulhu'); $fourthBook->method('getTitle')->willReturn('Art of War'); $this->repository->add($firstBook); $this->repository->add($secondBook); $this->repository->add($thirdBook); $this->repository->add($fourthBook); $this->repository->add($wrongIdBook); $this->repository->add($wrongNameBook); $this->assertSame( [$thirdBook, $firstBook], $this->repository->findBy( ['name' => $name, 'id' => $id], ['rating' => RepositoryInterface::ORDER_ASCENDING, 'title' => RepositoryInterface::ORDER_DESCENDING], 2, 1, ), ); } public function testItThrowsInvalidArgumentExceptionWhenFindingOneObjectWithEmptyParameterArray(): void { $this->expectException(\InvalidArgumentException::class); $this->repository->findOneBy([]); } public function testItFindsOneObjectByParameter(): void { $book = new SampleBookResource(); $book->name = 'Book'; $shirt = new SampleBookResource(); $shirt->name = 'Shirt'; $this->repository->add($book); $this->repository->add($shirt); $this->assertSame($book, $this->repository->findOneBy(['name' => 'Book'])); } public function testItReturnsFirstResultWhileFindingOneByParameters(): void { $book = new SampleBookResource(); $book->name = 'Book'; $secondBook = new SampleBookResource(); $secondBook->name = 'Book'; $this->repository->add($book); $this->repository->add($secondBook); $this->assertSame($book, $this->repository->findOneBy(['name' => 'Book'])); } public function testItFindsAllObjectsInMemory(): void { $book = $this->createMock(SampleBookResourceInterface::class); $shirt = $this->createMock(SampleBookResourceInterface::class); $this->repository->add($book); $this->repository->add($shirt); $this->assertSame([$book, $shirt], $this->repository->findAll()); } public function testItReturnsEmptyArrayWhenMemoryIsEmpty(): void { $this->assertSame([], $this->repository->findAll()); } public function testItCreatesPaginator(): void { $this->assertInstanceOf(Pagerfanta::class, $this->repository->createPaginator()); } public function testItReturnsStatedClassName(): void { $this->assertSame(SampleBookResourceInterface::class, $this->repository->getClassName()); } } interface SampleBookResourceInterface extends ResourceInterface { public function getName(): string; public function getRating(): int; public function getTitle(): string; } class SampleBookResource implements SampleBookResourceInterface { public $id; public $name; public $rating; public $title; public function getId(): mixed { return $this->id; } public function getName(): string { return $this->name; } public function getRating(): int { return $this->rating; } public function getTitle(): string { return $this->title; } } ================================================ FILE: src/Component/tests/Dummy/DummyResource.php ================================================ currentPlace; } public function setCurrentPlace(string $currentPlace): void { $this->currentPlace = $currentPlace; } } ================================================ FILE: src/Component/tests/Exception/RaceConditionExceptionTest.php ================================================ assertInstanceOf(UpdateHandlingException::class, $exception); } public function testItHasAMessage(): void { $exception = new RaceConditionException(); $this->assertSame('Operated entity was previously modified.', $exception->getMessage()); } public function testItHasAFlash(): void { $exception = new RaceConditionException(); $this->assertSame('race_condition_error', $exception->getFlash()); } public function testItHasAnApiResponseCode(): void { $exception = new RaceConditionException(); $this->assertSame(409, $exception->getApiResponseCode()); } } ================================================ FILE: src/Component/tests/Factory/FactoryTest.php ================================================ factory = new Factory(\stdClass::class); } /** @test */ public function it_implements_factory_interface(): void { $this->assertInstanceOf(FactoryInterface::class, $this->factory); } /** @test */ public function it_creates_a_new_instance_of_a_resource(): void { $this->assertInstanceOf(\stdClass::class, $this->factory->createNew()); } } ================================================ FILE: src/Component/tests/Factory/TranslatableFactoryTest.php ================================================ factory = $this->createMock(FactoryInterface::class); $this->localeProvider = $this->createMock(TranslationLocaleProviderInterface::class); $this->translatableFactory = new TranslatableFactory($this->factory, $this->localeProvider); } /** @test */ public function it_implements_translatable_factory_interface(): void { $this->assertInstanceOf(TranslatableFactoryInterface::class, $this->translatableFactory); } /** @test */ public function it_throws_an_exception_if_resource_is_not_translatable(): void { $this->factory->method('createNew')->willReturn(new \stdClass()); $this->expectException(UnexpectedTypeException::class); $this->translatableFactory->createNew(); } /** @test */ public function it_creates_translatable_and_sets_locales(): void { $resource = $this->createMock(TranslatableInterface::class); $this->localeProvider->method('getDefaultLocaleCode')->willReturn('pl_PL'); $this->factory->method('createNew')->willReturn($resource); $resource->expects($this->once())->method('setCurrentLocale')->with('pl_PL'); $resource->expects($this->once())->method('setFallbackLocale')->with('pl_PL'); $this->assertEquals($resource, $this->translatableFactory->createNew()); } } ================================================ FILE: src/Component/tests/Generator/RandomnessGeneratorTest.php ================================================ generator = new RandomnessGenerator(); } public function testItImplementsRandomnessGeneratorInterface(): void { $this->assertInstanceOf(RandomnessGeneratorInterface::class, $this->generator); } public function testItGeneratesRandomUriSafeStringOfLength(): void { $length = 9; $result = $this->generator->generateUriSafeString($length); $this->assertIsString($result); $this->assertSame($length, strlen($result)); } public function testItGeneratesRandomNumericStringOfLength(): void { $length = 12; $result = $this->generator->generateNumeric($length); $this->assertIsString($result); $this->assertIsNumeric($result); $this->assertSame($length, strlen($result)); } public function testItGeneratesRandomIntInRange(): void { $min = 12; $max = 2000000; $result = $this->generator->generateInt($min, $max); $this->assertIsInt($result); $this->assertGreaterThanOrEqual($min, $result); $this->assertLessThanOrEqual($max, $result); } } ================================================ FILE: src/Component/tests/Grid/State/RequestGridProviderTest.php ================================================ gridViewFactory = $this->createMock(GridViewFactoryInterface::class); $this->gridProvider = $this->createMock(GridProviderInterface::class); $this->provider = new RequestGridProvider( $this->gridViewFactory, $this->gridProvider, ); } public function testItIsInitializable(): void { $this->assertInstanceOf(RequestGridProvider::class, $this->provider); } public function testItProvidesAGridView(): void { $request = $this->createMock(Request::class); $context = new Context(new RequestOption($request)); $operation = new Index(grid: 'app_book'); $request->query = new InputBag(); $gridDefinition = $this->createMock(Grid::class); $gridView = $this->createMock(GridView::class); $this->gridProvider->method('get')->with('app_book')->willReturn($gridDefinition); $gridDefinition->method('getDriverConfiguration')->willReturn([]); $this->gridViewFactory->method('create')->with($gridDefinition, $context, new Parameters(), [])->willReturn($gridView); $this->assertSame($gridView, $this->provider->provide($operation, $context)); } public function testItSetsCurrentPageFromRequest(): void { $request = $this->createMock(Request::class); $context = new Context(new RequestOption($request)); $operation = new Index(grid: 'app_book'); $request->query = new InputBag(['page' => 42]); $gridDefinition = $this->createMock(Grid::class); $gridView = $this->createMock(GridView::class); $pagerfanta = $this->createMock(Pagerfanta::class); $this->gridProvider->method('get')->with('app_book')->willReturn($gridDefinition); $gridDefinition->method('getLimits')->willReturn([]); $gridDefinition->method('getDriverConfiguration')->willReturn([]); $this->gridViewFactory->method('create')->with($gridDefinition, $context, new Parameters(['page' => 42]), [])->willReturn($gridView); $gridView->method('getData')->willReturn($pagerfanta); $pagerfanta->expects($this->once())->method('setCurrentPage')->with(42)->willReturn($pagerfanta); $pagerfanta->expects($this->once())->method('setMaxPerPage')->with(10)->willReturn($pagerfanta); $this->assertSame($gridView, $this->provider->provide($operation, $context)); } public function testItSetsMaxPerPageFromRequest(): void { $request = $this->createMock(Request::class); $context = new Context(new RequestOption($request)); $operation = new Index(grid: 'app_book'); $request->query = new InputBag(['limit' => 25]); $gridDefinition = $this->createMock(Grid::class); $gridView = $this->createMock(GridView::class); $pagerfanta = $this->createMock(Pagerfanta::class); $this->gridProvider->method('get')->with('app_book')->willReturn($gridDefinition); $gridDefinition->method('getDriverConfiguration')->willReturn([]); $gridDefinition->method('getLimits')->willReturn([10, 25]); $this->gridViewFactory->method('create')->with($gridDefinition, $context, new Parameters(['limit' => 25]), [])->willReturn($gridView); $gridView->method('getData')->willReturn($pagerfanta); $pagerfanta->expects($this->once())->method('setCurrentPage')->with(1)->willReturn($pagerfanta); $pagerfanta->expects($this->once())->method('setMaxPerPage')->with(25)->willReturn($pagerfanta); $this->assertSame($gridView, $this->provider->provide($operation, $context)); } public function testItSetsMaxPerPageFromGridConfiguration(): void { $request = $this->createMock(Request::class); $context = new Context(new RequestOption($request)); $operation = new Index(grid: 'app_book'); $request->query = new InputBag(); $gridDefinition = $this->createMock(Grid::class); $gridView = $this->createMock(GridView::class); $pagerfanta = $this->createMock(Pagerfanta::class); $this->gridProvider->method('get')->with('app_book')->willReturn($gridDefinition); $gridDefinition->method('getDriverConfiguration')->willReturn([]); $gridDefinition->method('getLimits')->willReturn([15, 30]); $this->gridViewFactory->method('create')->with($gridDefinition, $context, new Parameters([]), [])->willReturn($gridView); $gridView->method('getData')->willReturn($pagerfanta); $pagerfanta->expects($this->once())->method('setCurrentPage')->with(1)->willReturn($pagerfanta); $pagerfanta->expects($this->once())->method('setMaxPerPage')->with(15)->willReturn($pagerfanta); $this->assertSame($gridView, $this->provider->provide($operation, $context)); } public function testItLimitsMaxPerPageWithMaxGridConfigurationLimit(): void { $request = $this->createMock(Request::class); $context = new Context(new RequestOption($request)); $operation = new Index(grid: 'app_book'); $request->query = new InputBag(['limit' => 40]); $gridDefinition = $this->createMock(Grid::class); $gridView = $this->createMock(GridView::class); $pagerfanta = $this->createMock(Pagerfanta::class); $this->gridProvider->method('get')->with('app_book')->willReturn($gridDefinition); $gridDefinition->method('getDriverConfiguration')->willReturn([]); $gridDefinition->method('getLimits')->willReturn([15, 30]); $this->gridViewFactory->method('create')->with($gridDefinition, $context, new Parameters(['limit' => 40]), [])->willReturn($gridView); $gridView->method('getData')->willReturn($pagerfanta); $pagerfanta->expects($this->once())->method('setCurrentPage')->with(1)->willReturn($pagerfanta); $pagerfanta->expects($this->once())->method('setMaxPerPage')->with(30)->willReturn($pagerfanta); $this->assertSame($gridView, $this->provider->provide($operation, $context)); } public function testItThrowsAnExceptionWhenOperationHasNoGrid(): void { $request = $this->createMock(Request::class); $operation = new Index(name: 'app_book'); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Operation has no grid, so you cannot use this provider for operation "app_book"'); $this->provider->provide($operation, new Context(new RequestOption($request))); } public function testItThrowsAnExceptionWhenOperationDoesNotImplementTheGridAwareInterface(): void { $request = $this->createMock(Request::class); $operation = new Create(name: 'app_book'); $this->expectException(\LogicException::class); $this->expectExceptionMessage('You can not use a grid if your operation does not implement "Sylius\Resource\Metadata\GridAwareOperationInterface".'); $this->provider->provide($operation, new Context(new RequestOption($request))); } } ================================================ FILE: src/Component/tests/Humanizer/StringHumanizerTest.php ================================================ assertInstanceOf(StringHumanizer::class, $humanizer); } public function testItHumanizesAString(): void { $humanizer = new StringHumanizer(); $this->assertSame('admin user', $humanizer::humanize('admin_user')); $this->assertSame('admin user', $humanizer::humanize('Admin_user')); $this->assertSame('admin user', $humanizer::humanize('AdminUser')); } } ================================================ FILE: src/Component/tests/Metadata/ApplyStateMachineTransitionTest.php ================================================ assertInstanceOf(ApplyStateMachineTransition::class, $operation); } public function testIsAnOperation(): void { $operation = new ApplyStateMachineTransition(); $this->assertInstanceOf(Operation::class, $operation); } public function testImplementsUpdateOperationInterface(): void { $operation = new ApplyStateMachineTransition(); $this->assertInstanceOf(UpdateOperationInterface::class, $operation); } public function testImplementsStateMachineAwareOperationInterface(): void { $operation = new ApplyStateMachineTransition(); $this->assertInstanceOf(StateMachineAwareOperationInterface::class, $operation); } public function testHasNoResourceByDefault(): void { $operation = new ApplyStateMachineTransition(); $this->assertNull($operation->getResource()); } public function testCouldHaveAResource(): void { $resource = new ResourceMetadata(alias: 'app.book'); $operation = new ApplyStateMachineTransition(); $operationWithResource = $operation->withResource($resource); $this->assertSame($resource, $operationWithResource->getResource()); } public function testHasBulkDeleteShortNameByDefault(): void { $operation = new ApplyStateMachineTransition(); $this->assertSame('apply_state_machine_transition', $operation->getShortName()); } public function testHasDeleteMethodsByDefault(): void { $operation = new ApplyStateMachineTransition(); $this->assertSame(['PUT', 'PATCH', 'POST'], $operation->getMethods()); } } ================================================ FILE: src/Component/tests/Metadata/BulkDeleteTest.php ================================================ assertInstanceOf(BulkDelete::class, $bulkDelete); } public function testItIsAnOperation(): void { $bulkDelete = new BulkDelete(); $this->assertInstanceOf(Operation::class, $bulkDelete); } public function testItImplementsDeleteOperationInterface(): void { $bulkDelete = new BulkDelete(); $this->assertInstanceOf(DeleteOperationInterface::class, $bulkDelete); } public function testItImplementsBulkOperationInterface(): void { $bulkDelete = new BulkDelete(); $this->assertInstanceOf(BulkOperationInterface::class, $bulkDelete); } public function testItHasNoResourceByDefault(): void { $bulkDelete = new BulkDelete(); $this->assertNull($bulkDelete->getResource()); } public function testItCouldHaveAResource(): void { $resource = new ResourceMetadata(alias: 'app.book'); $bulkDelete = (new BulkDelete())->withResource($resource); $this->assertSame($resource, $bulkDelete->getResource()); } public function testItHasBulkDeleteShortNameByDefault(): void { $bulkDelete = new BulkDelete(); $this->assertEquals('bulk_delete', $bulkDelete->getShortName()); } public function testItHasDeleteMethodsByDefault(): void { $bulkDelete = new BulkDelete(); $this->assertEquals(['DELETE', 'POST'], $bulkDelete->getMethods()); } } ================================================ FILE: src/Component/tests/Metadata/CreateTest.php ================================================ assertInstanceOf(Create::class, $create); } public function testItIsAnOperation(): void { $create = new Create(); $this->assertInstanceOf(Operation::class, $create); } public function testItImplementsCreateOperationInterface(): void { $create = new Create(); $this->assertInstanceOf(CreateOperationInterface::class, $create); } public function testItHasNoResourceByDefault(): void { $create = new Create(); $this->assertNull($create->getResource()); } public function testItCouldHaveAResource(): void { $resource = new ResourceMetadata('app.book'); $create = (new Create())->withResource($resource); $this->assertSame($resource, $create->getResource()); } public function testItHasCreateShortNameByDefault(): void { $create = new Create(); $this->assertSame('create', $create->getShortName()); } public function testItHasGetAndPostMethodsByDefault(): void { $create = new Create(); $this->assertSame(['GET', 'POST'], $create->getMethods()); } } ================================================ FILE: src/Component/tests/Metadata/DeleteTest.php ================================================ assertInstanceOf(Delete::class, $delete); } public function testItIsAnOperation(): void { $delete = new Delete(); $this->assertInstanceOf(Operation::class, $delete); } public function testItImplementsDeleteOperationInterface(): void { $delete = new Delete(); $this->assertInstanceOf(DeleteOperationInterface::class, $delete); } public function testItHasNoResourceByDefault(): void { $delete = new Delete(); $this->assertNull($delete->getResource()); } public function testItCouldHaveAResource(): void { $resource = new ResourceMetadata('app.book'); $delete = (new Delete())->withResource($resource); $this->assertSame($resource, $delete->getResource()); } public function testItHasDeleteShortNameByDefault(): void { $delete = new Delete(); $this->assertSame('delete', $delete->getShortName()); } public function testItHasDeleteMethodsByDefault(): void { $delete = new Delete(); $this->assertSame(['DELETE', 'POST'], $delete->getMethods()); } } ================================================ FILE: src/Component/tests/Metadata/Extractor/PhpFileResourceExtractorTest.php ================================================ assertEquals($expectedResources, $extractor->getResources()); } public function testItExcludesResourcesFromPhpFileThatDoesNotReturnResourceMetadata(): void { $extractor = new PhpFileResourceExtractor([__DIR__ . '/php/invalid_php_file.php']); $this->assertEquals([], $extractor->getResources()); } } ================================================ FILE: src/Component/tests/Metadata/Extractor/php/another_valid_php_file.php ================================================ assertInstanceOf(HttpOperation::class, $httpOperation); } public function testItHasNoNameByDefault(): void { $httpOperation = new HttpOperation(); $this->assertNull($httpOperation->getName()); } public function testItCouldHaveAName(): void { $httpOperation = (new HttpOperation())->withName('create'); $this->assertSame('create', $httpOperation->getName()); } public function testItHasNoMethodsByDefault(): void { $httpOperation = new HttpOperation(); $this->assertNull($httpOperation->getMethods()); } public function testItCouldHaveMethods(): void { $httpOperation = (new HttpOperation())->withMethods(['POST', 'GET']); $this->assertSame(['POST', 'GET'], $httpOperation->getMethods()); } public function testItHasNoPathByDefault(): void { $httpOperation = new HttpOperation(); $this->assertNull($httpOperation->getPath()); } public function testItCouldHaveAPath(): void { $httpOperation = (new HttpOperation())->withPath('you_should_not_pass'); $this->assertSame('you_should_not_pass', $httpOperation->getPath()); } public function testItHasNoRoutePrefixByDefault(): void { $httpOperation = new HttpOperation(); $this->assertNull($httpOperation->getRoutePrefix()); } public function testItCouldHaveARoutePrefix(): void { $httpOperation = (new HttpOperation())->withRoutePrefix('/admin'); $this->assertSame('/admin', $httpOperation->getRoutePrefix()); } public function testItHasNoRoutePriorityByDefault(): void { $httpOperation = new HttpOperation(); $this->assertNull($httpOperation->getROutePriority()); } public function testItCouldHaveARoutePriority(): void { $httpOperation = (new HttpOperation())->withRoutePriority(100); $this->assertSame(100, $httpOperation->getRoutePriority()); } public function testItHasNoTemplateByDefault(): void { $httpOperation = new HttpOperation(); $this->assertNull($httpOperation->getTemplate()); } public function testItCouldHaveATemplate(): void { $httpOperation = (new HttpOperation())->withTemplate('book/show.html.twig'); $this->assertSame('book/show.html.twig', $httpOperation->getTemplate()); } public function testItCanBeConstructedWithAName(): void { $httpOperation = new HttpOperation(name: 'create'); $this->assertSame('create', $httpOperation->getName()); } } ================================================ FILE: src/Component/tests/Metadata/IndexTest.php ================================================ index = new Index(); } public function testItIsInitializable(): void { $this->assertInstanceOf(Index::class, $this->index); } public function testItIsAnOperation(): void { $this->assertInstanceOf(Operation::class, $this->index); } public function testItImplementsCollectionOperationInterface(): void { $this->assertInstanceOf(CollectionOperationInterface::class, $this->index); } public function testItHasNoResourceByDefault(): void { $this->assertNull($this->index->getResource()); } public function testItCouldHaveAResource(): void { $resource = new ResourceMetadata(alias: 'app.book'); $this->index = $this->index->withResource($resource); $this->assertSame($resource, $this->index->getResource()); } public function testItHasIndexShortNameByDefault(): void { $this->assertSame('index', $this->index->getShortName()); } public function testItHasGetMethodsByDefault(): void { $this->assertSame(['GET'], $this->index->getMethods()); } } ================================================ FILE: src/Component/tests/Metadata/Inflector/InflectorTest.php ================================================ assertSame($expected, (new Inflector())->tableize($string)); } #[DataProvider('pluralizeProvider')] public function testPluralize(string $expected, string $string): void { $this->assertSame($expected, (new Inflector())->pluralize($string)); } #[DataProvider('dashizeProvider')] public function testDashize(string $expected, string $string): void { $this->assertSame($expected, (new Inflector())->dashize($string)); } public static function tableizeProvider(): iterable { yield ['book', 'Book']; yield ['stephen_king_book', 'StephenKingBook']; } public static function pluralizeProvider(): iterable { yield ['books', 'book']; yield ['products', 'product']; yield ['orders', 'order']; } public static function dashizeProvider(): iterable { yield ['stephen-king-book', 'StephenKingBook']; yield ['stephen-king-book', 'Stephen_King_Book']; yield ['stephen-king-book', 'Stephen King Book']; } } ================================================ FILE: src/Component/tests/Metadata/MetadataTest.php ================================================ metadata = Metadata::fromAliasAndConfiguration( 'app.product', [ 'driver' => 'doctrine/orm', 'state_machine_component' => 'symfony', 'templates' => 'product', 'classes' => [ 'model' => 'App\Model\Resource', 'form' => [ 'default' => 'App\Form\Type\ResourceType', 'choice' => 'App\Form\Type\ResourceChoiceType', 'autocomplete' => 'App\Type\ResourceAutocompleteType', ], ], ], ); } public function testItImplementsMetadataInterface(): void { $this->assertInstanceOf(MetadataInterface::class, $this->metadata); } public function testItHasAlias(): void { $this->assertSame('app.product', $this->metadata->getAlias()); } public function testItAllowsToHaveAliasWithDotInName(): void { $metadata = Metadata::fromAliasAndConfiguration( 'app.product.with.dots', [ 'driver' => 'doctrine/orm', 'templates' => 'product', 'classes' => [ 'model' => 'App\Model\Resource', 'form' => [ 'default' => 'App\Form\Type\ResourceType', 'choice' => 'App\Form\Type\ResourceChoiceType', 'autocomplete' => 'App\Type\ResourceAutocompleteType', ], ], ], ); $this->assertSame('app.product.with.dots', $metadata->getAlias()); $this->assertSame('app', $metadata->getApplicationName()); $this->assertSame('product.with.dots', $metadata->getName()); } public function testItHasApplicationName(): void { $this->assertSame('app', $this->metadata->getApplicationName()); } public function testItHasResourceName(): void { $this->assertSame('product', $this->metadata->getName()); } public function testItHumanizesSimpleNames(): void { $this->assertSame('product', $this->metadata->getHumanizedName()); } public function testItHumanizesMoreComplexNames(): void { $metadata = Metadata::fromAliasAndConfiguration('app.product_option', ['driver' => 'doctrine/orm']); $this->assertSame('product option', $metadata->getHumanizedName()); } public function testItHasPluralResourceName(): void { $this->assertSame('products', $this->metadata->getPluralName()); } public function testItHasDriver(): void { $this->assertSame('doctrine/orm', $this->metadata->getDriver()); } public function testItHasStateMachineComponent(): void { $this->assertSame('symfony', $this->metadata->getStateMachineComponent()); } public function testItHasTemplatesNamespace(): void { $this->assertSame('product', $this->metadata->getTemplatesNamespace()); } public function testItHasAccessToSpecificConfigParameter(): void { $this->assertSame('doctrine/orm', $this->metadata->getParameter('driver')); } public function testItChecksIfSpecificParameterExists(): void { $this->assertFalse($this->metadata->hasParameter('foo')); $this->assertTrue($this->metadata->hasParameter('driver')); } public function testItThrowsAnExceptionWhenParameterDoesNotExist(): void { $this->expectException(\InvalidArgumentException::class); $this->metadata->getParameter('foo'); } public function testItHasAccessToSpecificClasses(): void { $this->assertSame('App\Model\Resource', $this->metadata->getClass('model')); } public function testItThrowsAnExceptionWhenClassDoesNotExist(): void { $this->expectException(\InvalidArgumentException::class); $this->metadata->getClass('foo'); } public function testItChecksIfSpecificClassExists(): void { $this->assertFalse($this->metadata->hasClass('bar')); $this->assertTrue($this->metadata->hasClass('model')); } public function testItGeneratesServiceId(): void { $this->assertSame('app.factory.product', $this->metadata->getServiceId('factory')); $this->assertSame('app.repository.product', $this->metadata->getServiceId('repository')); $this->assertSame('app.form.type.product', $this->metadata->getServiceId('form.type')); } public function testItGeneratesPermissionCode(): void { $this->assertSame('app.product.show', $this->metadata->getPermissionCode('show')); $this->assertSame('app.product.create', $this->metadata->getPermissionCode('create')); $this->assertSame('app.product.custom', $this->metadata->getPermissionCode('custom')); } } ================================================ FILE: src/Component/tests/Metadata/Operation/DashPathSegmentNameGeneratorTest.php ================================================ assertSame($expected, (new DashPathSegmentNameGenerator(new Inflector()))->getSegmentName($name, $pluralize)); } public static function segmentNameProvider(): iterable { yield ['stephen-king-book', 'StephenKingBook', false]; yield ['stephen-king-book', 'Stephen_King_Book', false]; yield ['stephen-king-book', 'Stephen King Book', false]; yield ['stephen-king-books', 'StephenKingBook', true]; yield ['stephen-king-books', 'Stephen_King_Book', true]; yield ['stephen-king-books', 'Stephen King Book', true]; } } ================================================ FILE: src/Component/tests/Metadata/Operation/HttpOperationInitiatorTest.php ================================================ resourceRegistry = $this->createMock(RegistryInterface::class); $this->resourceMetadataCollectionFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $this->varsResolver = $this->createMock(VarsResolverInterface::class); } public function testItIsInitializable(): void { $initiator = new HttpOperationInitiator( $this->resourceRegistry, $this->resourceMetadataCollectionFactory, $this->varsResolver, ); $this->assertInstanceOf(HttpOperationInitiator::class, $initiator); } public function testItInitializesHttpOperationsFromRequest(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $metadata = $this->createMock(MetadataInterface::class); $operation = $this->createMock(HttpOperation::class); $request->attributes = $attributes; $attributes->method('get')->with('_route')->willReturn('app_dummy_index'); $attributes->method('all')->with('_sylius')->willReturn([ 'resource' => 'app.dummy', ]); $attributes->expects($this->once())->method('set')->with('_sylius', ['resource' => 'app.dummy', 'resource_class' => 'App\DummyResource']); $this->resourceRegistry->method('get')->with('app.dummy')->willReturn($metadata); $metadata->method('getClass')->with('model')->willReturn('App\DummyResource'); $metadata->method('getAlias')->willReturn('app.dummy'); $operation->method('getName')->willReturn('app_dummy_index'); $operation->method('getVars')->willReturn(null); $operation->method('getResource')->willReturn(null); $operations = new Operations(); $operations->add('app_dummy_index', $operation); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = (new ResourceMetadata(alias: 'app.dummy'))->withOperations($operations); $this->resourceMetadataCollectionFactory->method('create')->with('App\DummyResource')->willReturn($resourceMetadataCollection); $initiator = new HttpOperationInitiator( $this->resourceRegistry, $this->resourceMetadataCollectionFactory, ); $this->assertSame($operation, $initiator->initializeOperation($request)); } public function testItResolvesOperationVars(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $metadata = $this->createMock(MetadataInterface::class); $operation = new Index(name: 'app_dummy_index', vars: ['product' => '@=get_current_product()']); $request->attributes = $attributes; $attributes->method('get')->with('_route')->willReturn('app_dummy_index'); $attributes->method('all')->with('_sylius')->willReturn([ 'resource' => 'app.dummy', ]); $attributes->expects($this->once())->method('set')->with('_sylius', ['resource' => 'app.dummy', 'resource_class' => 'App\DummyResource']); $this->resourceRegistry->method('get')->with('app.dummy')->willReturn($metadata); $metadata->method('getClass')->with('model')->willReturn('App\DummyResource'); $metadata->method('getAlias')->willReturn('app.dummy'); $operations = new Operations(); $operations->add('app_dummy_index', $operation); $product = new \stdClass(); $this->varsResolver->expects($this->once())->method('resolve')->with(['product' => '@=get_current_product()'])->willReturn(['product' => $product]); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = (new ResourceMetadata(alias: 'app.dummy'))->withOperations($operations); $this->resourceMetadataCollectionFactory->method('create')->with('App\DummyResource')->willReturn($resourceMetadataCollection); $initiator = new HttpOperationInitiator( $this->resourceRegistry, $this->resourceMetadataCollectionFactory, $this->varsResolver, ); $result = $initiator->initializeOperation($request); $this->assertNotNull($result); $this->assertSame(['product' => $product], $result->getVars()); } public function testItReturnsNullWhenRequestHasNoSyliusOptions(): void { $request = $this->createMock(Request::class); $parameterBag = $this->createMock(ParameterBag::class); $request->attributes = $parameterBag; $parameterBag->method('get')->with('_route')->willReturn('app_dummy_index'); $parameterBag->expects($this->once())->method('all')->with('_sylius')->willReturn([]); $initiator = new HttpOperationInitiator( $this->resourceRegistry, $this->resourceMetadataCollectionFactory, ); $this->assertNull($initiator->initializeOperation($request)); } public function testItReturnsNullWhenRequestHasNoResourceOption(): void { $request = $this->createMock(Request::class); $parameterBag = $this->createMock(ParameterBag::class); $request->attributes = $parameterBag; $parameterBag->method('get')->with('_route')->willReturn('app_dummy_index'); $parameterBag->expects($this->once())->method('all')->with('_sylius')->willReturn([ 'foo' => 'bar', ]); $initiator = new HttpOperationInitiator( $this->resourceRegistry, $this->resourceMetadataCollectionFactory, ); $this->assertNull($initiator->initializeOperation($request)); } public function testItReturnsNullWhenRequestHasNoRoute(): void { $request = $this->createMock(Request::class); $parameterBag = $this->createMock(ParameterBag::class); $request->attributes = $parameterBag; $parameterBag->method('all')->with('_sylius')->willReturn([ 'resource' => 'app.dummy', ]); $parameterBag->expects($this->once())->method('get')->with('_route')->willReturn(null); $initiator = new HttpOperationInitiator( $this->resourceRegistry, $this->resourceMetadataCollectionFactory, ); $this->assertNull($initiator->initializeOperation($request)); } } ================================================ FILE: src/Component/tests/Metadata/Operation/UnderscorePathSegmentNameGeneratorTest.php ================================================ assertSame($expected, (new UnderscorePathSegmentNameGenerator(new Inflector()))->getSegmentName($name, $pluralize)); } public static function segmentNameProvider(): iterable { yield ['stephen_king_book', 'StephenKingBook', false]; yield ['stephen_king_book', 'Stephen_King_Book', false]; yield ['stephen_king_book', 'Stephen King Book', false]; yield ['stephen_king_books', 'StephenKingBook', true]; yield ['stephen_king_books', 'Stephen_King_Book', true]; yield ['stephen_king_books', 'Stephen King Book', true]; } } ================================================ FILE: src/Component/tests/Metadata/OperationsTest.php ================================================ operations = new Operations(); } public function testItIsInitializable(): void { $this->assertInstanceOf(Operations::class, $this->operations); } public function testItIsCountable(): void { $this->assertInstanceOf(\Countable::class, $this->operations); } public function testItIsAnIterator(): void { $this->assertInstanceOf(\IteratorAggregate::class, $this->operations); } public function testItAddsOperations(): void { $this->operations->add('create', new Create()); $this->assertTrue($this->operations->has('create')); } public function testItRemovesOperations(): void { $this->operations->add('create', new Create()); $this->operations->remove('create'); $this->assertFalse($this->operations->has('create')); } public function testItMergesOperations(): void { $this->operations->add('create', new Create()); $this->operations->add('create', new Create(name: 'new_name')); $this->assertCount(1, $this->operations); $this->assertTrue($this->operations->has('create')); $this->assertSame('new_name', $this->operations->getIterator()->current()->getName()); } public function testItThrowsARuntimeExceptionWhenRemovingNotFoundOperation(): void { $this->expectException(\RuntimeException::class); $this->operations->remove('not_found_operation'); } public function testItReturnsOperationsCount(): void { $this->operations->add('create', new Create()); $this->operations->add('update', new Update()); $this->assertCount(2, $this->operations); } } ================================================ FILE: src/Component/tests/Metadata/RegistryTest.php ================================================ registry = new Registry(); } public function testItImplementsRegistryInterface(): void { $this->assertInstanceOf(RegistryInterface::class, $this->registry); } public function testItReturnsAllResourcesMetadata(): void { $metadata1 = $this->createMock(MetadataInterface::class); $metadata2 = $this->createMock(MetadataInterface::class); $metadata1->method('getAlias')->willReturn('app.product'); $metadata2->method('getAlias')->willReturn('app.order'); $this->registry->add($metadata1); $this->registry->add($metadata2); $this->assertSame([ 'app.product' => $metadata1, 'app.order' => $metadata2, ], $this->registry->getAll()); } public function testItThrowsAnExceptionIfResourceIsNotRegistered(): void { $this->expectException(\InvalidArgumentException::class); $this->registry->get('foo.bar'); } public function testItReturnsSpecificMetadata(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata->method('getAlias')->willReturn('app.shipping_method'); $this->registry->add($metadata); $this->assertSame($metadata, $this->registry->get('app.shipping_method')); } public function testItThrowsAnExceptionIfResourceIsNotRegisteredWithClass(): void { $this->expectException(\InvalidArgumentException::class); $this->registry->getByClass('App\Model\OrderItem'); } public function testItReturnsSpecificMetadataByModelClass(): void { $metadata1 = $this->createMock(MetadataInterface::class); $metadata2 = $this->createMock(MetadataInterface::class); $metadata1->method('getAlias')->willReturn('app.product'); $metadata1->method('getClass')->with('model')->willReturn('App\Model\Product'); $metadata2->method('getAlias')->willReturn('app.order'); $metadata2->method('getClass')->with('model')->willReturn('App\Model\Order'); $this->registry->add($metadata1); $this->registry->add($metadata2); $this->assertSame($metadata2, $this->registry->getByClass('App\Model\Order')); } public function testItAddsMetadataFromConfigurationArray(): void { $this->registry->addFromAliasAndConfiguration('app.product', [ 'driver' => 'doctrine/orm', 'classes' => [ 'model' => 'App\Model\Product', ], ]); $this->assertInstanceOf(MetadataInterface::class, $this->registry->get('app.product')); $this->assertInstanceOf(MetadataInterface::class, $this->registry->getByClass('App\Model\Product')); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/AttributesResourceClassListFactoryTest.php ================================================ [dirname(__DIR__, 3) . '/Dummy']], ); $list = $attributesResourceClassListFactory->create(); $this->assertContains(DummyResource::class, $list->getIterator()); $this->assertNotContains(PullRequest::class, $list->getIterator()); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php ================================================ resourceRegistry = $this->createMock(RegistryInterface::class); $this->factory = new AttributesResourceMetadataCollectionFactory($this->resourceRegistry, new OperationRouteNameFactory()); } public function testItIsInitializable(): void { $this->assertInstanceOf(AttributesResourceMetadataCollectionFactory::class, $this->factory); } public function testItCreatesResourceMetadata(): void { $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); $this->resourceRegistry->method('get')->willReturn($metadata); $metadataCollection = $this->factory->create(DummyResourceWithAlias::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $this->assertCount(1, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); } public function testItCreatesResourceMetadataWithoutResourceAlias(): void { $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); $this->resourceRegistry->method('getByClass')->willReturn($metadata); $metadataCollection = $this->factory->create(DummyResource::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $this->assertCount(1, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $this->assertSame(DummyResource::class, $resource->getClass()); } public function testItCreatesResourceMetadataWithOperations(): void { $metadata = Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ]); $this->resourceRegistry->method('get')->willReturn($metadata); $metadataCollection = $this->factory->create(DummyResourceWithOperations::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(4, $operations); $this->assertTrue($operations->has('app_dummy_index')); $this->assertTrue($operations->has('app_dummy_create')); $this->assertTrue($operations->has('app_dummy_update')); $this->assertTrue($operations->has('app_dummy_show')); } public function testItCreatesMultiResourcesMetadataWithOperations(): void { $orderMetadata = Metadata::fromAliasAndConfiguration('app.order', [ 'driver' => 'order_driver', 'classes' => [ 'model' => 'App\Order', 'form' => 'App\Form\OrderType', ], ]); $cartMetadata = Metadata::fromAliasAndConfiguration('app.cart', [ 'driver' => 'cart_driver', 'classes' => [ 'model' => 'App\Cart', 'form' => 'App\Form\CartType', ], ]); $this->resourceRegistry->method('get') ->willReturnMap([ ['app.order', $orderMetadata], ['app.cart', $cartMetadata], ]); $metadataCollection = $this->factory->create(DummyMultiResourcesWithOperations::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $this->assertCount(2, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.order', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(2, $operations); $this->assertTrue($operations->has('app_order_index')); $this->assertTrue($operations->has('app_order_create')); $operation = $metadataCollection->getOperation('app.order', 'app_order_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame('app_order_index', $operation->getName()); $this->assertSame(['GET'], $operation->getMethods()); $this->assertSame('app.repository.order', $operation->getRepository()); $this->assertSame('App\Form\OrderType', $operation->getFormType()); $operation = $metadataCollection->getOperation('app.cart', 'app_cart_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame('app_cart_index', $operation->getName()); $this->assertSame(['GET'], $operation->getMethods()); $this->assertSame('app.repository.cart', $operation->getRepository()); $this->assertSame('App\Form\CartType', $operation->getFormType()); $operation = $metadataCollection->getOperation('app.cart', 'app_cart_show'); $this->assertInstanceOf(Show::class, $operation); $this->assertSame('app_cart_show', $operation->getName()); $this->assertSame(['GET'], $operation->getMethods()); $this->assertSame('app.repository.cart', $operation->getRepository()); $this->assertSame('App\Form\CartType', $operation->getFormType()); } public function testItCreatesMultiResourcesMetadataWithSections(): void { $metadata = Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ]); $this->resourceRegistry->method('get')->willReturn($metadata); $metadataCollection = $this->factory->create(DummyResourceWithSections::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $this->assertCount(2, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(2, $operations); $this->assertTrue($operations->has('app_admin_dummy_index')); $this->assertTrue($operations->has('app_admin_dummy_create')); $operation = $metadataCollection->getOperation('app.dummy', 'app_admin_dummy_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame('app_admin_dummy_index', $operation->getName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_admin_dummy_create'); $this->assertInstanceOf(Create::class, $operation); $this->assertSame('app_admin_dummy_create', $operation->getName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_shop_dummy_show'); $this->assertInstanceOf(Show::class, $operation); $this->assertSame('app_shop_dummy_show', $operation->getName()); } public function testItCreatesMultiResourcesMetadataWithSectionsAndNestedOperations(): void { if (\PHP_VERSION_ID < 80100) { $this->markTestSkipped('Nested attributes are supported since PHP 8.1'); } $metadata = Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ]); $this->resourceRegistry->method('get')->willReturn($metadata); $metadataCollection = $this->factory->create(DummyResourceWithSectionsAndNestedOperations::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $this->assertCount(2, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(2, $operations); $this->assertTrue($operations->has('app_admin_dummy_index')); $this->assertTrue($operations->has('app_admin_dummy_create')); $operation = $metadataCollection->getOperation('app.dummy', 'app_admin_dummy_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame('app_admin_dummy_index', $operation->getName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_admin_dummy_create'); $this->assertInstanceOf(Create::class, $operation); $this->assertSame('app_admin_dummy_create', $operation->getName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_admin_dummy_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame('app_admin_dummy_index', $operation->getName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_shop_dummy_show'); $this->assertInstanceOf(Show::class, $operation); $this->assertSame('app_shop_dummy_show', $operation->getName()); } public function testItCreatesOperationsEvenIfThereIsNoResourceAttribute(): void { $this->resourceRegistry ->method('getByClass') ->with(DummyOperationsWithoutResource::class) ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver'])); $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyOperationsWithoutResource::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $this->assertCount(1, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(2, $operations); $this->assertTrue($operations->has('app_dummy_index')); $this->assertTrue($operations->has('app_dummy_create')); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame('app_dummy_index', $operation->getName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertInstanceOf(Create::class, $operation); $this->assertSame('app_dummy_create', $operation->getName()); } public function testItCreatesResourceMetadataWithFormType(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => ['model' => 'App\Dummy'], ])); $metadataCollection = $this->factory->create(DummyResourceWithFormType::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(2, $operations); $this->assertTrue($operations->has('app_dummy_create')); $this->assertTrue($operations->has('app_dummy_update')); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertSame('App\Form\DummyType', $operation->getFormType()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertSame('App\Form\DummyType', $operation->getFormType()); } public function testItCreatesResourceMetadataWithFormOptions(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyResourceWithFormOptions::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(2, $operations); $this->assertTrue($operations->has('app_dummy_create')); $this->assertTrue($operations->has('app_dummy_update')); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertInstanceOf(Create::class, $operation); $this->assertSame([ 'data_class' => 'App\Dummy', 'html5' => false, ], $operation->getFormOptions()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertInstanceOf(Update::class, $operation); $this->assertSame([ 'data_class' => 'App\Dummy', 'html5' => true, ], $operation->getFormOptions()); } public function testItCreatesResourceMetadataWithValidationContext(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyResourceWithValidationContext::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(2, $operations); $this->assertTrue($operations->has('app_dummy_create')); $this->assertTrue($operations->has('app_dummy_update')); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertInstanceOf(Create::class, $operation); $this->assertSame(['groups' => ['sylius']], $operation->getValidationContext()); $this->assertSame([ 'validation_groups' => ['sylius'], 'data_class' => 'App\Dummy', ], $operation->getFormOptions()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertInstanceOf(Update::class, $operation); $this->assertSame(['groups' => ['sylius']], $operation->getValidationContext()); $this->assertSame([ 'validation_groups' => ['sylius'], 'data_class' => 'App\Dummy', ], $operation->getFormOptions()); } public function testItCreatesResourceMetadataWithResourceName(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyResourceWithName::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(4, $operations); $this->assertTrue($operations->has('app_book_create')); $this->assertTrue($operations->has('app_book_update')); $this->assertTrue($operations->has('app_book_index')); $this->assertTrue($operations->has('app_book_show')); $operation = $metadataCollection->getOperation('app.dummy', 'app_book_create'); $this->assertSame('book', $operation->getResource()->getName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_book_update'); $this->assertSame('book', $operation->getResource()->getName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_book_index'); $this->assertSame('book', $operation->getResource()->getName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_book_show'); $this->assertSame('book', $operation->getResource()->getName()); } public function testItCreatesResourceMetadataWithResourcePluralName(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyResourceWithPluralName::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(4, $operations); $this->assertTrue($operations->has('app_dummy_create')); $this->assertTrue($operations->has('app_dummy_update')); $this->assertTrue($operations->has('app_dummy_index')); $this->assertTrue($operations->has('app_dummy_show')); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertSame('books', $operation->getResource()->getPluralName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertSame('books', $operation->getResource()->getPluralName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_index'); $this->assertSame('books', $operation->getResource()->getPluralName()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_show'); $this->assertSame('books', $operation->getResource()->getPluralName()); } public function testItCreatesResourceMetadataWithDefaultResponderOnHttpOperations(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyResourceWithOperations::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(4, $operations); $this->assertTrue($operations->has('app_dummy_create')); $this->assertTrue($operations->has('app_dummy_update')); $this->assertTrue($operations->has('app_dummy_index')); $this->assertTrue($operations->has('app_dummy_show')); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertSame(Responder::class, $operation->getResponder()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertSame(Responder::class, $operation->getResponder()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_index'); $this->assertSame(Responder::class, $operation->getResponder()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_show'); $this->assertSame(Responder::class, $operation->getResponder()); } public function testItCreatesResourceMetadataWithRoutePrefix(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyResourceWithRoutePrefix::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(4, $operations); $this->assertTrue($operations->has('app_dummy_create')); $this->assertTrue($operations->has('app_dummy_update')); $this->assertTrue($operations->has('app_dummy_index')); $this->assertTrue($operations->has('app_dummy_show')); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertInstanceOf(Create::class, $operation); $this->assertSame('/admin', $operation->getRoutePrefix()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertInstanceOf(Update::class, $operation); $this->assertSame('/admin', $operation->getRoutePrefix()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame('/admin', $operation->getRoutePrefix()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_show'); $this->assertInstanceOf(Show::class, $operation); $this->assertSame('/admin', $operation->getRoutePrefix()); } public function testItCreatesResourceMetadataWithRouteCondition(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyResourceWithRouteCondition::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertSame('custom_condition', $operation->getRouteCondition()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertSame('custom_condition', $operation->getRouteCondition()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_index'); $this->assertSame('custom_condition', $operation->getRouteCondition()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_show'); $this->assertSame('custom_condition', $operation->getRouteCondition()); } public function testItCreatesResourceMetadataWithRouteRequirements(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', ])); $metadataCollection = $this->factory->create(DummyResourceWithRouteRequirements::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertSame(['_locale' => 'en|fr|pl'], $operation->getRouteRequirements()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertSame(['_locale' => 'en|fr|pl'], $operation->getRouteRequirements()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_index'); $this->assertSame(['_locale' => 'en|fr|pl'], $operation->getRouteRequirements()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_show'); $this->assertSame(['_locale' => 'en|fr|pl', 'page' => '\d+'], $operation->getRouteRequirements()); } public function testItCreatesResourceMetadataWithRoutePriorities(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', ])); $metadataCollection = $this->factory->create(DummyResourceWithRoutePriorities::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertSame(1, $operation->getRoutePriority()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertSame(1, $operation->getRoutePriority()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_index'); $this->assertSame(1, $operation->getRoutePriority()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_show'); $this->assertSame(-100, $operation->getRoutePriority()); } public function testItCreatesResourceMetadataWithNormalizationContext(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyResourceWithNormalizationContext::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(4, $operations); $this->assertTrue($operations->has('app_dummy_create')); $this->assertTrue($operations->has('app_dummy_update')); $this->assertTrue($operations->has('app_dummy_index')); $this->assertTrue($operations->has('app_dummy_show')); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertInstanceOf(Create::class, $operation); $this->assertSame(['groups' => ['dummy:read']], $operation->getNormalizationContext()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertInstanceOf(Update::class, $operation); $this->assertSame(['groups' => ['dummy:read']], $operation->getNormalizationContext()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame(['groups' => ['dummy:read']], $operation->getNormalizationContext()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_show'); $this->assertInstanceOf(Show::class, $operation); $this->assertSame(['groups' => ['dummy:read']], $operation->getNormalizationContext()); } public function testItCreatesResourceMetadataWithDenormalizationContext(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyResourceWithDenormalizationContext::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(4, $operations); $this->assertTrue($operations->has('app_dummy_create')); $this->assertTrue($operations->has('app_dummy_update')); $this->assertTrue($operations->has('app_dummy_index')); $this->assertTrue($operations->has('app_dummy_show')); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertInstanceOf(Create::class, $operation); $this->assertSame(['groups' => ['dummy:write']], $operation->getDenormalizationContext()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertInstanceOf(Update::class, $operation); $this->assertSame(['groups' => ['dummy:write']], $operation->getDenormalizationContext()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame(['groups' => ['dummy:write']], $operation->getDenormalizationContext()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_show'); $this->assertInstanceOf(Show::class, $operation); $this->assertSame(['groups' => ['dummy:write']], $operation->getDenormalizationContext()); } public function testItCreatesResourceMetadataWithDefaultTwigContextFactory(): void { $this->resourceRegistry ->method('get') ->with('app.dummy') ->willReturn(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', 'classes' => [ 'model' => 'App\Dummy', 'form' => 'App\Form', ], ])); $metadataCollection = $this->factory->create(DummyResourceWithOperations::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('app.dummy', $resource->getAlias()); $operations = $resource->getOperations(); $this->assertInstanceOf(Operations::class, $operations); $this->assertCount(4, $operations); $this->assertTrue($operations->has('app_dummy_create')); $this->assertTrue($operations->has('app_dummy_update')); $this->assertTrue($operations->has('app_dummy_index')); $this->assertTrue($operations->has('app_dummy_show')); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_create'); $this->assertInstanceOf(Create::class, $operation); $this->assertSame('sylius.twig.context.factory.default', $operation->getTwigContextFactory()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_update'); $this->assertInstanceOf(Update::class, $operation); $this->assertSame('sylius.twig.context.factory.default', $operation->getTwigContextFactory()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame('sylius.twig.context.factory.default', $operation->getTwigContextFactory()); $operation = $metadataCollection->getOperation('app.dummy', 'app_dummy_show'); $this->assertInstanceOf(Show::class, $operation); $this->assertSame('sylius.twig.context.factory.default', $operation->getTwigContextFactory()); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/CachedResourceMetadataCollectionFactoryTest.php ================================================ cacheItemPool = $this->createMock(CacheItemPoolInterface::class); $this->decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); } public function testItIsInitializable(): void { $factory = new CachedResourceMetadataCollectionFactory( $this->cacheItemPool, $this->decorated, ); $this->assertInstanceOf(CachedResourceMetadataCollectionFactory::class, $factory); } public function testItUsesDecoratedFactoryWhenCacheIsNotAvailable(): void { $cacheItem = $this->createMock(CacheItemInterface::class); $resourceMetadataCollection = new ResourceMetadataCollection(); $this->cacheItemPool->method('getItem')->willReturn($cacheItem); $cacheItem->method('isHit')->willReturn(false); $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $cacheItem->expects($this->once())->method('set')->with((array) $resourceMetadataCollection)->willReturn($cacheItem); $this->cacheItemPool->expects($this->once())->method('save')->with($cacheItem)->willReturn(true); $factory = new CachedResourceMetadataCollectionFactory( $this->cacheItemPool, $this->decorated, ); $this->assertSame($resourceMetadataCollection, $factory->create('App\Resource')); } public function testItRetrievesCacheWhenItIsAvailable(): void { $cacheItem = $this->createMock(CacheItemInterface::class); $resourceMetadataCollection = new ResourceMetadataCollection(); $this->cacheItemPool->method('getItem')->willReturn($cacheItem); $cacheItem->method('isHit')->willReturn(true); $cacheItem->method('get')->willReturn($resourceMetadataCollection); $factory = new CachedResourceMetadataCollectionFactory( $this->cacheItemPool, $this->decorated, ); $result = $factory->create('App\Resource'); $this->assertInstanceOf(ResourceMetadataCollection::class, $result); } public function testItUsesLocalCacheWhenCachePoolIsNotAvailable(): void { $resourceMetadataCollection = new ResourceMetadataCollection(); $this->cacheItemPool->method('getItem')->willThrowException(new CacheException()); $this->decorated->expects($this->once())->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $factory = new CachedResourceMetadataCollectionFactory( $this->cacheItemPool, $this->decorated, ); $this->assertSame($resourceMetadataCollection, $factory->create('App\Resource')); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/EventShortNameResourceMetadataCollectionFactoryTest.php ================================================ decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $this->factory = new EventShortNameResourceMetadataCollectionFactory($this->decorated); } public function testItIsInitializable(): void { $this->assertInstanceOf(EventShortNameResourceMetadataCollectionFactory::class, $this->factory); } public function testItConfiguresDefaultEventShortNameOnOperations(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $create = (new Create(name: 'app_book_create'))->withResource($resource); $show = (new Show(name: 'app_book_show'))->withResource($resource); $applyStateMachineTransition = (new ApplyStateMachineTransition(name: 'app_book_publish', stateMachineTransition: 'publish'))->withResource($resource); $bulkDelete = (new BulkDelete(name: 'app_book_bulk_delete'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, $show->getName() => $show, $applyStateMachineTransition->getName() => $applyStateMachineTransition, $bulkDelete->getName() => $bulkDelete, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $this->factory->create('App\Resource'); $create = $resourceMetadataCollection->getOperation('app.book', 'app_book_create'); $this->assertSame('create', $create->getEventShortName()); $show = $resourceMetadataCollection->getOperation('app.book', 'app_book_show'); $this->assertSame('show', $show->getEventShortName()); $publish = $resourceMetadataCollection->getOperation('app.book', 'app_book_publish'); $this->assertSame('update', $publish->getEventShortName()); $bulkDelete = $resourceMetadataCollection->getOperation('app.book', 'app_book_bulk_delete'); $this->assertSame('delete', $bulkDelete->getEventShortName()); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/FactoryResourceMetadataCollectionFactoryTest.php ================================================ resourceRegistry = $this->createMock(RegistryInterface::class); $this->decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $this->factory = new FactoryResourceMetadataCollectionFactory($this->resourceRegistry, $this->decorated); } public function testItIsInitializable(): void { $this->assertInstanceOf(FactoryResourceMetadataCollectionFactory::class, $this->factory); } public function testItConfiguresFactoryIfOperationImplementsTheInterface(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $create = (new Create(name: 'app_book_create'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceConfiguration = $this->createMock(MetadataInterface::class); $resourceConfiguration->method('getDriver')->willReturn('doctrine/orm'); $resourceConfiguration->method('getServiceId')->with('factory')->willReturn('app.factory.book'); $this->resourceRegistry->method('get')->with('app.book')->willReturn($resourceConfiguration); $resourceMetadataCollection = $this->factory->create('App\Resource'); $create = $resourceMetadataCollection->getOperation('app.book', 'app_book_create'); $this->assertSame('app.factory.book', $create->getFactory()); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/MutatorResourceMetadataCollectionFactoryTest.php ================================================ createMock(ResourceMetadataCollectionFactoryInterface::class); $resourceClass = \stdClass::class; $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = (new ResourceMetadata())->withClass($resourceClass); $resourceMutatorCollection = new ResourceMutatorCollection(); $resourceMutatorCollection->add($resourceClass, new DummyResourceMutator()); $customResourceMetadataCollectionFactory = new MutatorResourceMetadataCollectionFactory($resourceMutatorCollection, new OperationMutatorCollection(), $decorated); $decorated->expects($this->once())->method('create')->with($resourceClass)->willReturn( $resourceMetadataCollection, ); $resourceMetadataCollection = $customResourceMetadataCollectionFactory->create($resourceClass); $resource = $resourceMetadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame('custom_dummy', $resource->getName()); } public function testMutateOperation(): void { $decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $resourceClass = \stdClass::class; $operations = new Operations(); $operations->add('app_dummy_index', new HttpOperation()); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = (new ResourceMetadata(alias: 'app.dummy'))->withClass($resourceClass)->withOperations($operations); $operationMutatorCollection = new OperationMutatorCollection(); $operationMutatorCollection->add('app_dummy_index', new DummyOperationMutator()); $customResourceMetadataCollectionFactory = new MutatorResourceMetadataCollectionFactory(new ResourceMutatorCollection(), $operationMutatorCollection, $decorated); $decorated->expects($this->once())->method('create')->with($resourceClass)->willReturn( $resourceMetadataCollection, ); $resourceMetadataCollection = $customResourceMetadataCollectionFactory->create($resourceClass); $resource = $resourceMetadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertEquals('custom_dummy', $resourceMetadataCollection->getOperation('app.dummy', 'app_dummy_index')->getShortName()); } } final class DummyResourceMutator implements ResourceMutatorInterface { public function __invoke(ResourceMetadata $resource): ResourceMetadata { return $resource->withName('custom_dummy'); } } final class DummyOperationMutator implements OperationMutatorInterface { public function __invoke(Operation $operation): Operation { return $operation->withShortName('custom_dummy'); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/PhpFileResourceClassListFactoryTest.php ================================================ getExtractor(), ); $list = $attributesResourceClassListFactory->create(); $this->assertCount(1, $list->getIterator()); $this->assertContains(\stdClass::class, $list->getIterator()); } private function getExtractor(): ResourceExtractorInterface { return new PhpFileResourceExtractor([ __DIR__ . '/php/php_file_with_resource_class.php', __DIR__ . '/php/php_file_without_resource_class.php', ]); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/PhpFileResourceMetadataCollectionFactoryTest.php ================================================ resourceRegistry = $this->createMock(RegistryInterface::class); $this->factory = new PhpFileResourceMetadataCollectionFactory($this->resourceRegistry, new OperationRouteNameFactory(), new PhpFileResourceExtractor([__DIR__ . '/php/php_file_with_resource_class.php'])); } public function testItIsInitializable(): void { $this->assertInstanceOf(PhpFileResourceMetadataCollectionFactory::class, $this->factory); } public function testItCreatesResourceMetadata(): void { $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); $this->resourceRegistry->method('get')->willReturn($metadata); $metadataCollection = $this->factory->create(\stdClass::class); $this->assertInstanceOf(ResourceMetadataCollection::class, $metadataCollection); $this->assertCount(1, $metadataCollection); $resource = $metadataCollection->getIterator()->current(); $this->assertInstanceOf(ResourceMetadata::class, $resource); $this->assertSame(\stdClass::class, $resource->getClass()); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/PluralNameResourceMetadataCollectionFactoryTest.php ================================================ decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $this->registry = $this->createMock(RegistryInterface::class); } public function testItConfiguresDefaultPluralNameOnResourcesWithRoutingBcLayerEnabled(): void { $factory = new PluralNameResourceMetadataCollectionFactory(decorated: $this->decorated, inflector: new Inflector(), resourceRegistry: $this->registry, routingBcLayerEnabled: true); $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $this->registry->method('get')->with('app.book')->willReturn(Metadata::fromAliasAndConfiguration('app.book', ['driver' => 'doctrine/orm'])); $resourceMetadataCollection = $factory->create('App\Resource'); $resourceOutput = $resourceMetadataCollection[0]; $this->assertInstanceOf(ResourceMetadata::class, $resourceOutput); $this->assertSame('books', $resourceOutput->getPluralName()); } public function testItConfiguresDefaultPluralNameOnResourcesWithRoutingBcLayerDisabled(): void { $factory = new PluralNameResourceMetadataCollectionFactory(decorated: $this->decorated, inflector: new Inflector(), routingBcLayerEnabled: false); $resource = new ResourceMetadata(name: 'book'); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $factory->create('App\Resource'); $resourceOutput = $resourceMetadataCollection[0]; $this->assertInstanceOf(ResourceMetadata::class, $resourceOutput); $this->assertSame('books', $resourceOutput->getPluralName()); } public function testItThrowAnExceptionWithRoutingBcLayerEnabledWhenResourceRegistryIsNotPassedAsConstructorArguments(): void { $factory = new PluralNameResourceMetadataCollectionFactory(decorated: $this->decorated, inflector: new Inflector(), routingBcLayerEnabled: true); $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $this->expectException(LogicException::class); $this->expectExceptionMessage(sprintf( 'Routing Bc-Layer is enabled, but the resource registry is not passed as constructor arguments of "%s" class.', PluralNameResourceMetadataCollectionFactory::class, )); $factory->create('App\Resource'); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/ProviderResourceMetadataCollectionFactoryTest.php ================================================ decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $this->factory = new ProviderResourceMetadataCollectionFactory($this->decorated); } public function testItIsInitializable(): void { $this->assertInstanceOf(ProviderResourceMetadataCollectionFactory::class, $this->factory); } public function testItCreatesResourceMetadataWithDefaultProviderOnHttpOperations(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $index = (new Index(name: 'app_book_index'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $index->getName() => $index, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $this->factory->create('App\Resource'); $index = $resourceMetadataCollection->getOperation('app.book', 'app_book_index'); $this->assertSame(Provider::class, $index->getProvider()); } public function testItConfiguresRequestGridProviderIfOperationHasAGrid(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $index = (new Index(name: 'app_book_index', grid: 'app_book'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $index->getName() => $index, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $this->factory->create('App\Resource'); $index = $resourceMetadataCollection->getOperation('app.book', 'app_book_index'); $this->assertSame(RequestGridProvider::class, $index->getProvider()); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/RedirectResourceMetadataCollectionFactoryTest.php ================================================ decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $this->factory = new RedirectResourceMetadataCollectionFactory( new OperationRouteNameFactory(), $this->decorated, ); } public function testItIsInitializable(): void { $this->assertInstanceOf(RedirectResourceMetadataCollectionFactory::class, $this->factory); } public function testItRedirectsCreateToShowIfRouteExists(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $create = (new Create(name: 'app_book_create'))->withResource($resource); $show = (new Show(name: 'app_book_show'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, $show->getName() => $show, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $this->factory->create('App\Resource'); $create = $resourceMetadataCollection->getOperation('app.book', 'app_book_create'); $this->assertSame('app_book_show', $create->getRedirectToRoute()); } public function testItRedirectsCreateToIndexIfRouteExists(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $create = (new Create(name: 'app_book_create'))->withResource($resource); $index = (new Index(name: 'app_book_index'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, $index->getName() => $index, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $this->factory->create('App\Resource'); $create = $resourceMetadataCollection->getOperation('app.book', 'app_book_create'); $this->assertSame('app_book_index', $create->getRedirectToRoute()); } public function testItRedirectsUpdateToShowIfRouteExists(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $update = (new Update(name: 'app_book_update'))->withResource($resource); $show = (new Show(name: 'app_book_show'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $update->getName() => $update, $show->getName() => $show, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $this->factory->create('App\Resource'); $update = $resourceMetadataCollection->getOperation('app.book', 'app_book_update'); $this->assertSame('app_book_show', $update->getRedirectToRoute()); } public function testItRedirectsUpdateToIndexIfRouteExists(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $update = (new Update(name: 'app_book_update'))->withResource($resource); $index = (new Index(name: 'app_book_index'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $update->getName() => $update, $index->getName() => $index, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $this->factory->create('App\Resource'); $update = $resourceMetadataCollection->getOperation('app.book', 'app_book_update'); $this->assertSame('app_book_index', $update->getRedirectToRoute()); } public function testItRedirectsDeleteToIndexIfRouteExists(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $delete = (new Delete(name: 'app_book_delete'))->withResource($resource); $index = (new Show(name: 'app_book_index'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $delete->getName() => $delete, $index->getName() => $index, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $this->factory->create('App\Resource'); $delete = $resourceMetadataCollection->getOperation('app.book', 'app_book_delete'); $this->assertSame('app_book_index', $delete->getRedirectToRoute()); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/StateMachineResourceMetadataCollectionFactoryTest.php ================================================ resourceRegistry = $this->createMock(RegistryInterface::class); $this->decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $this->factory = new StateMachineResourceMetadataCollectionFactory($this->resourceRegistry, $this->decorated, null); } public function testItIsInitializable(): void { $this->assertInstanceOf(StateMachineResourceMetadataCollectionFactory::class, $this->factory); } public function testItSetsTheDefaultStateMachineComponentFromSettings(): void { $this->factory = new StateMachineResourceMetadataCollectionFactory($this->resourceRegistry, $this->decorated, 'symfony'); $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $create = (new Create(name: 'app_book_create'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceConfiguration = $this->createMock(MetadataWithStateMachineInterface::class); $resourceConfiguration->method('getStateMachineComponent')->willReturn(null); $this->resourceRegistry->method('get')->with('app.book')->willReturn($resourceConfiguration); $resourceMetadataCollection = $this->factory->create('App\Resource'); $create = $resourceMetadataCollection->getOperation('app.book', 'app_book_create'); $this->assertEquals('symfony', $create->getStateMachineComponent()); } public function testItSetsTheDefaultStateMachineComponentFromResourceConfiguration(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $create = (new Create(name: 'app_book_create'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceConfiguration = $this->createMock(MetadataWithStateMachineInterface::class); $resourceConfiguration->method('getStateMachineComponent')->willReturn('symfony'); $this->resourceRegistry->method('get')->with('app.book')->willReturn($resourceConfiguration); $resourceMetadataCollection = $this->factory->create('App\Resource'); $create = $resourceMetadataCollection->getOperation('app.book', 'app_book_create'); $this->assertEquals('symfony', $create->getStateMachineComponent()); } public function testItSetsTheConfiguredStateMachineComponentFromOperation(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $create = (new Create(name: 'app_book_create', stateMachineComponent: 'winzou'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceConfiguration = $this->createMock(MetadataWithStateMachineInterface::class); $resourceConfiguration->method('getStateMachineComponent')->willReturn('symfony'); $this->resourceRegistry->method('get')->with('app.book')->willReturn($resourceConfiguration); $resourceMetadataCollection = $this->factory->create('App\Resource'); $create = $resourceMetadataCollection->getOperation('app.book', 'app_book_create'); $this->assertEquals('winzou', $create->getStateMachineComponent()); } public function testItConfiguresApplyStateMachineTransitionProcessorIfOperationHasATransition(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $create = (new Create(name: 'app_book_create', stateMachineTransition: 'publish'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceConfiguration = $this->createMock(MetadataWithStateMachineInterface::class); $resourceConfiguration->method('getStateMachineComponent')->willReturn('symfony'); $this->resourceRegistry->method('get')->with('app.book')->willReturn($resourceConfiguration); $resourceMetadataCollection = $this->factory->create('App\Resource'); $create = $resourceMetadataCollection->getOperation('app.book', 'app_book_create'); $this->assertEquals(ApplyStateMachineTransitionProcessor::class, $create->getProcessor()); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/TemplatesDirResourceMetadataCollectionFactoryTest.php ================================================ decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $this->factory = new TemplatesDirResourceMetadataCollectionFactory($this->decorated); } public function testItIsInitializable(): void { $this->assertInstanceOf(TemplatesDirResourceMetadataCollectionFactory::class, $this->factory); } public function testItUsesDefaultTemplatesDir(): void { $this->factory = new TemplatesDirResourceMetadataCollectionFactory($this->decorated, ['default_templates_dir' => 'crud']); $resource = new ResourceMetadata(alias: 'app.book'); $create = (new Create(name: 'app_book_create'))->withResource($resource); $show = (new Show(name: 'app_book_show'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, $show->getName() => $show, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $result = $this->factory->create('App\Resource'); $create = $result->getOperation('app.book', 'app_book_create'); $this->assertSame('crud/create.html.twig', $create->getTemplate()); $show = $result->getOperation('app.book', 'app_book_show'); $this->assertSame('crud/show.html.twig', $show->getTemplate()); } public function testItUsesResourceTemplatesDir(): void { $resource = new ResourceMetadata(alias: 'app.book', templatesDir: 'book'); $create = (new Create(name: 'app_book_create'))->withResource($resource); $show = (new Show(name: 'app_book_show'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, $show->getName() => $show, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $result = $this->factory->create('App\Resource'); $create = $result->getOperation('app.book', 'app_book_create'); $this->assertSame('book/create.html.twig', $create->getTemplate()); $show = $result->getOperation('app.book', 'app_book_show'); $this->assertSame('book/show.html.twig', $show->getTemplate()); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/VarsResourceMetadataCollectionFactoryTest.php ================================================ decorated = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $this->argumentParser = $this->createMock(ArgumentParserInterface::class); $this->factory = new VarsResourceMetadataCollectionFactory($this->decorated, $this->argumentParser); } public function testItIsInitializable(): void { $this->assertInstanceOf(VarsResourceMetadataCollectionFactory::class, $this->factory); } public function testItMergesResourceVarsWithOperationVars(): void { $resource = new ResourceMetadata( alias: 'app.book', name: 'book', applicationName: 'app', vars: ['header' => 'Library', 'subheader' => 'Managing your library'], ); $create = (new Create(name: 'app_book_create', vars: ['subheader' => 'Adding a new book']))->withResource($resource); $show = (new Show(name: 'app_book_show'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, $show->getName() => $show, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $this->factory->create('App\Resource'); $create = $resourceMetadataCollection->getOperation('app.book', 'app_book_create'); $this->assertSame([ 'header' => 'Library', 'subheader' => 'Adding a new book', ], $create->getVars()); } public function testItDoesNothingWhenResourceHasNoVars(): void { $resource = new ResourceMetadata( alias: 'app.book', name: 'book', applicationName: 'app', ); $create = (new Create(name: 'app_book_create'))->withResource($resource); $show = (new Show(name: 'app_book_show'))->withResource($resource); $resource = $resource->withOperations(new Operations([ $create->getName() => $create, $show->getName() => $show, ])); $resourceMetadataCollection = new ResourceMetadataCollection(); $resourceMetadataCollection[] = $resource; $this->decorated->method('create')->with('App\Resource')->willReturn($resourceMetadataCollection); $resourceMetadataCollection = $this->factory->create('App\Resource'); $create = $resourceMetadataCollection->getOperation('app.book', 'app_book_create'); $this->assertNull($create->getVars()); } } ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/php/php_file_with_resource_class.php ================================================ withOperations(new Operations([ new Create(), ])); ================================================ FILE: src/Component/tests/Metadata/Resource/Factory/php/php_file_without_resource_class.php ================================================ assertInstanceOf(\IteratorAggregate::class, $list); } public function testItIsCountable(): void { $list = new ResourceClassList(); $this->assertInstanceOf(\Countable::class, $list); } public function testItIsAListOfResourceClassNames(): void { $list = new ResourceClassList(['first_resource', 'second_resource']); $this->assertCount(2, $list); $this->assertEquals('first_resource', $list->getIterator()[0]); $this->assertEquals('second_resource', $list->getIterator()[1]); } } ================================================ FILE: src/Component/tests/Metadata/Resource/ResourceMetadataCollectionTest.php ================================================ withOperations( new Operations([ 'app_dummy_index' => new Index(), 'app_dummy_create' => new Create(), ]), ); $this->collection = new ResourceMetadataCollection([$resourceMetadata]); } public function testItIsInitializable(): void { $this->assertInstanceOf(ResourceMetadataCollection::class, $this->collection); } public function testItCanGetAResourceOperation(): void { $operation = $this->collection->getOperation('app.dummy', 'app_dummy_index'); $this->assertInstanceOf(Index::class, $operation); } public function testItThrowsAnExceptionWhenOperationWasNotFound(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Operation "app_dummy_not_found" for "app.dummy" resource was not found.'); $this->collection->getOperation('app.dummy', 'app_dummy_not_found'); } } ================================================ FILE: src/Component/tests/Metadata/ResourceMetadataTest.php ================================================ resourceMetadata = new ResourceMetadata(); } public function testItIsInitializable(): void { $this->assertInstanceOf(ResourceMetadata::class, $this->resourceMetadata); } public function testItHasNoAliasByDefault(): void { $this->assertNull($this->resourceMetadata->getAlias()); } public function testItCouldHaveAnAlias(): void { $this->resourceMetadata = $this->resourceMetadata->withAlias('app.book'); $this->assertSame('app.book', $this->resourceMetadata->getAlias()); } public function testItHasNoSectionByDefault(): void { $this->assertNull($this->resourceMetadata->getSection()); } public function testItCouldHaveASection(): void { $this->resourceMetadata = $this->resourceMetadata->withSection('admin'); $this->assertSame('admin', $this->resourceMetadata->getSection()); } public function testItHasNoNameByDefault(): void { $this->assertNull($this->resourceMetadata->getName()); } public function testItCouldHaveAName(): void { $this->resourceMetadata = $this->resourceMetadata->withName('book'); $this->assertSame('book', $this->resourceMetadata->getName()); } public function testItHasNoApplicationNameByDefault(): void { $this->assertNull($this->resourceMetadata->getApplicationName()); } public function testItCouldHaveAnApplicationName(): void { $this->resourceMetadata = $this->resourceMetadata->withApplicationName('app'); $this->assertSame('app', $this->resourceMetadata->getApplicationName()); } public function testItHasNoOperationsByDefault(): void { $this->assertNull($this->resourceMetadata->getOperations()); } public function testItCouldHaveOperations(): void { $operations = new Operations(); $this->resourceMetadata = $this->resourceMetadata->withOperations($operations); $this->assertSame($operations, $this->resourceMetadata->getOperations()); } public function testItCanBeConstructedWithAnAlias(): void { $this->resourceMetadata = new ResourceMetadata('app.book'); $this->assertSame('app.book', $this->resourceMetadata->getAlias()); } public function testItCanBeConstructedWithASection(): void { $this->resourceMetadata = new ResourceMetadata(null, 'admin'); $this->assertSame('admin', $this->resourceMetadata->getSection()); } public function testItCanBeConstructedWithAName(): void { $this->resourceMetadata = new ResourceMetadata(name: 'book'); $this->assertSame('book', $this->resourceMetadata->getName()); } public function testItCanBeConstructedWithAnApplicationName(): void { $this->resourceMetadata = new ResourceMetadata(applicationName: 'app'); $this->assertSame('app', $this->resourceMetadata->getApplicationName()); } public function testItCanBeConstructedWithAFormType(): void { $this->resourceMetadata = new ResourceMetadata(formType: 'App\Form\DummyType'); $this->assertSame('App\Form\DummyType', $this->resourceMetadata->getFormType()); } public function testItCanBeConstructedWithATemplatesDir(): void { $this->resourceMetadata = new ResourceMetadata(templatesDir: 'book'); $this->assertSame('book', $this->resourceMetadata->getTemplatesDir()); } public function testItCanBeConstructedWithARoutePrefix(): void { $this->resourceMetadata = new ResourceMetadata(routePrefix: '/admin'); $this->assertSame('/admin', $this->resourceMetadata->getRoutePrefix()); } public function testItCanBeConstructedWithAPluralName(): void { $this->resourceMetadata = new ResourceMetadata(pluralName: 'books'); $this->assertSame('books', $this->resourceMetadata->getPluralName()); } public function testItCanBeConstructedWithAnIdentifier(): void { $this->resourceMetadata = new ResourceMetadata(identifier: 'code'); $this->assertSame('code', $this->resourceMetadata->getIdentifier()); } public function testItCanBeConstructedWithANormalizationContext(): void { $this->resourceMetadata = new ResourceMetadata( normalizationContext: ['groups' => ['dummy:read']], ); $this->assertSame(['groups' => ['dummy:read']], $this->resourceMetadata->getNormalizationContext()); } public function testItCanBeConstructedWithADenormalizationContext(): void { $this->resourceMetadata = new ResourceMetadata( denormalizationContext: ['groups' => ['dummy:write']], ); $this->assertSame(['groups' => ['dummy:write']], $this->resourceMetadata->getDenormalizationContext()); } public function testItCanBeConstructedWithAValidationContext(): void { $this->resourceMetadata = new ResourceMetadata( validationContext: ['groups' => ['sylius']], ); $this->assertSame(['groups' => ['sylius']], $this->resourceMetadata->getValidationContext()); } public function testItCanBeConstructedWithAClass(): void { $this->resourceMetadata = new ResourceMetadata( class: 'App\Resource', ); $this->assertSame('App\Resource', $this->resourceMetadata->getClass()); } public function testItCanBeConstructedWithADriver(): void { $this->resourceMetadata = new ResourceMetadata( driver: 'doctrine/orm', ); $this->assertSame('doctrine/orm', $this->resourceMetadata->getDriver()); } public function testItCanBeConstructedWithVars(): void { $this->resourceMetadata = new ResourceMetadata( vars: ['subheader' => 'Managing your library'], ); $this->assertSame(['subheader' => 'Managing your library'], $this->resourceMetadata->getVars()); } public function testItCanBeConstructedWithOperations(): void { $operations = [new Create(), new Update()]; $this->resourceMetadata = new ResourceMetadata( operations: $operations, ); $this->assertCount(2, $this->resourceMetadata->getOperations()); } } ================================================ FILE: src/Component/tests/Metadata/ShowTest.php ================================================ show = new Show(); } public function testItIsInitializable(): void { $this->assertInstanceOf(Show::class, $this->show); } public function testItIsAnOperation(): void { $this->assertInstanceOf(Operation::class, $this->show); } public function testItImplementsShowOperationInterface(): void { $this->assertInstanceOf(ShowOperationInterface::class, $this->show); } public function testItHasNoResourceByDefault(): void { $this->assertNull($this->show->getResource()); } public function testItCouldHaveAResource(): void { $resource = new ResourceMetadata(alias: 'app.book'); $this->show = $this->show->withResource($resource); $this->assertSame($resource, $this->show->getResource()); } public function testItHasShowShortNameByDefault(): void { $this->assertSame('show', $this->show->getShortName()); } public function testItHasGetMethodsByDefault(): void { $this->assertSame(['GET'], $this->show->getMethods()); } } ================================================ FILE: src/Component/tests/Metadata/UpdateTest.php ================================================ update = new Update(); } public function testItIsInitializable(): void { $this->assertInstanceOf(Update::class, $this->update); } public function testItIsAnOperation(): void { $this->assertInstanceOf(Operation::class, $this->update); } public function testItImplementsUpdateOperationInterface(): void { $this->assertInstanceOf(UpdateOperationInterface::class, $this->update); } public function testItHasNoResourceByDefault(): void { $this->assertNull($this->update->getResource()); } public function testItCouldHaveAResource(): void { $resource = new ResourceMetadata(alias: 'app.book'); $this->update = $this->update->withResource($resource); $this->assertSame($resource, $this->update->getResource()); } public function testItHasUpdateNameByDefault(): void { $this->assertSame('update', $this->update->getShortName()); } public function testItHasGetAndPutMethodsByDefault(): void { $this->assertSame(['GET', 'PUT', 'POST'], $this->update->getMethods()); } } ================================================ FILE: src/Component/tests/Model/AbstractTranslationTest.php ================================================ translation = new ConcreteTranslation(); } public function testItIsATranslation(): void { $this->assertInstanceOf(TranslationInterface::class, $this->translation); } public function testItsTranslatableIsMutable(): void { $translatable = $this->createMock(TranslatableInterface::class); $this->translation->setTranslatable($translatable); $this->assertSame($translatable, $this->translation->getTranslatable()); } public function testItsDetachesFromItsTranslatableCorrectly(): void { $translatable1 = $this->createMock(TranslatableInterface::class); $translatable2 = $this->createMock(TranslatableInterface::class); $translatable1->expects($this->once())->method('addTranslation')->with($this->isInstanceOf(AbstractTranslation::class)); $this->translation->setTranslatable($translatable1); $translatable1->expects($this->once())->method('removeTranslation')->with($this->isInstanceOf(AbstractTranslation::class)); $translatable2->expects($this->once())->method('addTranslation')->with($this->isInstanceOf(AbstractTranslation::class)); $this->translation->setTranslatable($translatable2); } public function testItsLocaleIsMutable(): void { $this->translation->setLocale('en'); $this->assertSame('en', $this->translation->getLocale()); } } class ConcreteTranslation extends AbstractTranslation { } ================================================ FILE: src/Component/tests/Reflection/CallableReflectionTest.php ================================================ assertInstanceOf(CallableReflection::class, $callableReflection); } public function testItReflectsAnArrayCallable(): void { $reflection = CallableReflection::from([RepositoryWithCallables::class, 'find']); $this->assertInstanceOf(\ReflectionFunctionAbstract::class, $reflection); } public function testItReflectsAClosureCallable(): void { $reflection = CallableReflection::from(fn (): array => []); $this->assertInstanceOf(\ReflectionFunctionAbstract::class, $reflection); } public function testItReflectsAStringCallable(): void { $reflection = CallableReflection::from('Sylius\Component\Resource\Tests\Dummy\RepositoryWithCallables::find'); $this->assertInstanceOf(\ReflectionFunctionAbstract::class, $reflection); } public function testItReflectsAnInvokableCallable(): void { $reflection = CallableReflection::from(new RepositoryWithCallables()); $this->assertInstanceOf(\ReflectionFunctionAbstract::class, $reflection); } } ================================================ FILE: src/Component/tests/Reflection/Filter/FunctionArgumentsFilterTest.php ================================================ functionArgumentsFilter = new FunctionArgumentsFilter(); } public function testItIsInitializable(): void { $this->assertInstanceOf(FunctionArgumentsFilter::class, $this->functionArgumentsFilter); } public function testItFiltersMatchingArguments(): void { $callable = [RepositoryWithCallables::class, 'find']; $reflector = CallableReflection::from($callable); $result = $this->functionArgumentsFilter->filter( $reflector, [ 'id' => 'my_id', 'foo' => 'fighters', ], ); $this->assertSame(['id' => 'my_id'], $result); } } ================================================ FILE: src/Component/tests/Reflection/ReflectionClassRecursiveIteratorTest.php ================================================ tmpDir = sys_get_temp_dir() . '/reflection_iterator_' . uniqid('', true); mkdir($this->tmpDir, 0777, true); } protected function tearDown(): void { $this->removeDirectory($this->tmpDir); parent::tearDown(); } public function testItReturnsReflectionClassesFromGivenDirectories(): void { $this->createPhpFile( $this->tmpDir . '/Foo.php', <<<'PHP' createPhpFile( $this->tmpDir . '/Bar.php', <<<'PHP' tmpDir, ]); self::assertArrayHasKey('Test\\Fixtures\\Foo', $reflections); self::assertArrayHasKey('Test\\Fixtures\\Bar', $reflections); self::assertInstanceOf(\ReflectionClass::class, $reflections['Test\\Fixtures\\Foo']); self::assertInstanceOf(\ReflectionClass::class, $reflections['Test\\Fixtures\\Bar']); self::assertSame( realpath($this->tmpDir . '/Foo.php'), $reflections['Test\\Fixtures\\Foo']->getFileName(), ); } public function testItUsesLocalCacheForSameDirectories(): void { $this->createPhpFile( $this->tmpDir . '/Cached.php', <<<'PHP' tmpDir, ]); $secondCall = ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories([ $this->tmpDir, ]); // Same array instance returned from cache self::assertSame($firstCall, $secondCall); self::assertArrayHasKey('Test\\Fixtures\\Cached', $secondCall); } public function testItIgnoresInvalidPhpFiles(): void { $this->createPhpFile( $this->tmpDir . '/Invalid.php', <<<'PHP' tmpDir, ]); self::assertSame([], $reflections); } public function testItOnlyIncludesFilesMatchingIgnoreRegex(): void { $this->createPhpFile( $this->tmpDir . '/IncludedOne.php', <<<'PHP' createPhpFile( $this->tmpDir . '/Excluded.php', <<<'PHP' tmpDir], '.*Included', ); self::assertArrayHasKey('Test\\Fixtures\\IncludedOne', $reflections); self::assertArrayNotHasKey('Test\\Fixtures\\Excluded', $reflections); } private function createPhpFile(string $path, string $contents): void { file_put_contents($path, $contents); } private function removeDirectory(string $directory): void { if (!is_dir($directory)) { return; } $files = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST, ); foreach ($files as $file) { $file->isDir() ? rmdir($file->getRealPath()) : unlink($file->getRealPath()); } rmdir($directory); } } ================================================ FILE: src/Component/tests/State/FactoryTest.php ================================================ locator = $this->createMock(ContainerInterface::class); $this->argumentParser = $this->createMock(ArgumentParserInterface::class); $this->factory = new Factory($this->locator, $this->argumentParser); } public function testItIsInitializable(): void { $this->assertInstanceOf(Factory::class, $this->factory); } public function testItCallsFactoryFromOperationAsCallable(): void { $operation = new Create(factory: [FactoryCallable::class, 'create']); $result = $this->factory->create($operation, new Context()); $this->assertInstanceOf(\stdClass::class, $result); } public function testItCallsFactoryWithArgumentsFromOperationAsCallable(): void { $operation = new Create(factory: [FactoryCallable::class, 'create'], factoryArguments: ['userId' => 'user.getUserIdentifier()']); $this->argumentParser->expects($this->once()) ->method('parseExpression') ->with('user.getUserIdentifier()') ->willReturn('51353e91-5295-4876-a994-cae4b3ff3a7c'); $result = $this->factory->create($operation, new Context()); $this->assertInstanceOf(\stdClass::class, $result); $this->assertEquals('51353e91-5295-4876-a994-cae4b3ff3a7c', $result->userId); } public function testItCallsFactoryWithExpressionLanguagePrefixedArgumentsFromOperationAsCallable(): void { $operation = new Create(factory: [FactoryCallable::class, 'create'], factoryArguments: ['userId' => '@=user.getUserIdentifier()']); $this->argumentParser->expects($this->once()) ->method('parseExpression') ->with('user.getUserIdentifier()') ->willReturn('51353e91-5295-4876-a994-cae4b3ff3a7c'); $result = $this->factory->create($operation, new Context()); $this->assertInstanceOf(\stdClass::class, $result); $this->assertEquals('51353e91-5295-4876-a994-cae4b3ff3a7c', $result->userId); } public function testItCallsFactoryFromOperationAsString(): void { $factory = $this->createMock(FactoryInterface::class); $operation = new Create(name: 'app_dummy_create', factory: get_class($factory), factoryMethod: 'createNew'); $data = new \stdClass(); $this->locator->expects($this->once()) ->method('has') ->with(get_class($factory)) ->willReturn(true); $this->locator->expects($this->once()) ->method('get') ->with(get_class($factory)) ->willReturn($factory); $factory->expects($this->once()) ->method('createNew') ->willReturn($data); $result = $this->factory->create($operation, new Context()); $this->assertSame($data, $result); } public function testItThrowsExceptionWhenFactoryIsNotFoundOnLocator(): void { $factory = $this->createMock(FactoryInterface::class); $operation = new Create(name: 'app_dummy_create', factory: get_class($factory), factoryMethod: 'createNew'); $this->locator->expects($this->once()) ->method('has') ->with(get_class($factory)) ->willReturn(false); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage(sprintf('Factory "%s" not found on operation "app_dummy_create"', get_class($factory))); $this->factory->create($operation, new Context()); } public function testItThrowsExceptionWhenFactoryMethodIsNull(): void { $factory = $this->createMock(FactoryInterface::class); $operation = new Create(name: 'app_dummy_create', factory: get_class($factory)); $this->locator->expects($this->once()) ->method('has') ->with(get_class($factory)) ->willReturn(true); $this->locator->expects($this->once()) ->method('get') ->with(get_class($factory)) ->willReturn($factory); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('No Factory method was configured on operation "app_dummy_create"'); $this->factory->create($operation, new Context()); } } final class FactoryCallable { public static function create(?string $userId = null): object { $object = new \stdClass(); $object->userId = $userId; return $object; } } ================================================ FILE: src/Component/tests/State/Processor/BulkAwareProcessorTest.php ================================================ processor = $this->createMock(ProcessorInterface::class); $this->bulkAwareProcessor = new BulkAwareProcessor($this->processor); } public function testItIsInitializable(): void { $this->assertInstanceOf(BulkAwareProcessor::class, $this->bulkAwareProcessor); } public function testItCallsDecoratedProcessorForEachDataForBulkOperation(): void { $firstItem = new \stdClass(); $secondItem = new \stdClass(); $operation = new BulkDelete(); $context = new Context(); $this->processor->expects($this->exactly(2)) ->method('process') ->willReturnCallback(function () { return null; }); $data = [$firstItem, $secondItem]; $this->bulkAwareProcessor->process($data, $operation, $context); } public function testItCallsDecoratedProcessorForDataForOtherOperationThanBulkOne(): void { $data = new \stdClass(); $result = new \stdClass(); $operation = new Delete(); $context = new Context(); $this->processor->expects($this->once()) ->method('process') ->with($data, $operation, $context) ->willReturn($result); $this->assertSame($result, $this->bulkAwareProcessor->process($data, $operation, $context)); } } ================================================ FILE: src/Component/tests/State/Processor/EventDispatcherBulkAwareProcessorTest.php ================================================ decorated = $this->createMock(ProcessorInterface::class); $this->operationEventDispatcher = $this->createMock(OperationEventDispatcherInterface::class); $this->processor = new EventDispatcherBulkAwareProcessor( $this->decorated, $this->operationEventDispatcher, ); } public function testItIsInitializable(): void { $this->assertInstanceOf(EventDispatcherBulkAwareProcessor::class, $this->processor); } public function testItDispatchesEventsForBulkOperation(): void { $operation = new BulkDelete(processor: [ProcessorWithCallable::class, 'process']); $context = new Context(); $operationEvent = new OperationEvent(); $data = []; $this->operationEventDispatcher->expects($this->once()) ->method('dispatchBulkEvent') ->with($data, $operation, $context) ->willReturn($operationEvent); $this->decorated->expects($this->once()) ->method('process') ->with($data, $operation, $context) ->willReturn(null); $this->processor->process($data, $operation, $context); } } ================================================ FILE: src/Component/tests/State/Processor/FlashProcessorTest.php ================================================ decorated = $this->createMock(ProcessorInterface::class); $this->flashHelper = $this->createMock(FlashHelperInterface::class); $this->flashProcessor = new FlashProcessor( $this->decorated, $this->flashHelper, ); } /** @test */ public function it_adds_success_flash(): void { $request = $this->createMock(Request::class); $operation = $this->createMock(HttpOperation::class); $request->attributes = new ParameterBag(); $request->method('getRequestFormat')->willReturn('html'); $request->method('isMethodSafe')->willReturn(false); $operation->method('canWrite')->willReturn(null); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('process') ->with(['foo' => 'fighters'], $operation, $context) ->willReturn(['foo' => 'fighters']) ; $this->flashHelper->expects($this->once()) ->method('addSuccessFlash') ->with($operation, $context) ; $this->flashProcessor->process(['foo' => 'fighters'], $operation, $context); } /** @test */ public function it_adds_error_flash(): void { $request = $this->createMock(Request::class); $operation = $this->createMock(HttpOperation::class); $request->attributes = new ParameterBag(['error' => 'Cannot delete, the resource is in use.']); $request->method('getRequestFormat')->willReturn('html'); $request->method('isMethodSafe')->willReturn(false); $operation->method('canWrite')->willReturn(null); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('process') ->with(['foo' => 'fighters'], $operation, $context) ->willReturn(['foo' => 'fighters']) ; $this->flashHelper->expects($this->once()) ->method('addErrorFlash') ->with($operation, $context) ; $this->flashProcessor->process(['foo' => 'fighters'], $operation, $context); } /** @test */ public function it_does_nothing_when_controller_result_is_a_response(): void { $request = $this->createMock(Request::class); $operation = $this->createMock(HttpOperation::class); $response = $this->createMock(Response::class); $request->method('getRequestFormat')->willReturn('html'); $request->expects($this->never())->method('isMethodSafe'); $operation->expects($this->never())->method('canWrite'); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('process') ->with($response, $operation, $context) ->willReturn($response) ; $this->flashHelper->expects($this->never())->method('addSuccessFlash'); $this->flashProcessor->process($response, $operation, $context); } /** @test */ public function it_does_nothing_when_method_is_safe(): void { $request = $this->createMock(Request::class); $operation = $this->createMock(HttpOperation::class); $request->method('getRequestFormat')->willReturn('html'); $request->method('isMethodSafe')->willReturn(true); $operation->expects($this->never())->method('canWrite'); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('process') ->with(['foo' => 'fighters'], $operation, $context) ->willReturn(['foo' => 'fighters']) ; $this->flashHelper->expects($this->never())->method('addSuccessFlash'); $this->flashProcessor->process(['foo' => 'fighters'], $operation, $context); } /** @test */ public function it_does_nothing_when_operation_cannot_be_written(): void { $request = $this->createMock(Request::class); $operation = $this->createMock(HttpOperation::class); $request->method('getRequestFormat')->willReturn('html'); $request->method('isMethodSafe')->willReturn(false); $operation->method('canWrite')->willReturn(false); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('process') ->with(['foo' => 'fighters'], $operation, $context) ->willReturn(['foo' => 'fighters']) ; $this->flashHelper->expects($this->never())->method('addSuccessFlash'); $this->flashProcessor->process(['foo' => 'fighters'], $operation, $context); } } ================================================ FILE: src/Component/tests/State/Processor/RespondProcessorTest.php ================================================ responder = $this->createMock(ResponderInterface::class); $this->respondProcessor = new RespondProcessor( $this->responder, ); } /** @test */ public function it_returns_a_response(): void { $response = $this->createMock(Response::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(); $this->responder->expects($this->once()) ->method('respond') ->with(['foo' => 'fighters'], $operation, $context) ->willReturn($response); $data = $this->respondProcessor->process(['foo' => 'fighters'], $operation, $context); Assert::eq($data, $response); } /** @test */ public function it_does_nothing_when_data_is_a_response(): void { $response = $this->createMock(Response::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(); $this->responder->expects($this->never()) ->method('respond'); $data = $this->respondProcessor->process($response, $operation, $context); Assert::eq($data, $response); } } ================================================ FILE: src/Component/tests/State/Processor/WriteProcessorTest.php ================================================ processor = $this->createMock(ProcessorInterface::class); $this->locatorProcessor = $this->createMock(ProcessorInterface::class); $this->writeProcessor = new WriteProcessor( $this->processor, $this->locatorProcessor, ); } /** @test */ public function it_calls_locator_processor_to_write_data(): void { $data = ['foo' => 'fighters']; $operation = new Create(processor: 'App\Processor'); $context = new Context(); $processedData = new \stdClass(); $response = new Response(); $this->locatorProcessor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($processedData); $this->processor->expects($this->once())->method('process')->with($processedData, $operation, $context)->willReturn($response); $result = $this->writeProcessor->process($data, $operation, $context); $this->assertEquals($response, $result); } /** @test */ public function it_does_not_call_locator_processor_when_data_is_a_response(): void { $data = new Response(); $operation = new Create(processor: 'App\Processor'); $context = new Context(); $this->locatorProcessor->expects($this->never())->method('process'); $this->processor->expects($this->once())->method('process')->willReturn($data); $this->writeProcessor->process($data, $operation, $context); } /** @test */ public function it_does_not_call_locator_processor_when_operation_cannot_be_written(): void { $data = ['foo' => 'fighters']; $operation = new Create(processor: 'App\Processor', write: false); $context = new Context(); $this->locatorProcessor->expects($this->never())->method('process'); $this->processor->expects($this->once())->method('process')->willReturn($data); $this->writeProcessor->process($data, $operation, $context); } /** @test */ public function it_does_not_call_locator_processor_when_operation_has_no_processor(): void { $data = ['foo' => 'fighters']; $operation = new Create(processor: null); $context = new Context(); $this->locatorProcessor->expects($this->never())->method('process'); $this->processor->expects($this->once())->method('process')->willReturn($data); $this->writeProcessor->process($data, $operation, $context); } } ================================================ FILE: src/Component/tests/State/ProcessorTest.php ================================================ locator = $this->createMock(ContainerInterface::class); $this->processor = new Processor($this->locator); } public function testItIsInitializable(): void { $this->assertInstanceOf(Processor::class, $this->processor); } public function testItCallsProcessMethodFromOperationProcessorAsString(): void { $operation = new Create(processor: '\App\Processor'); $context = new Context(); $processor = $this->createMock(ProcessorInterface::class); $this->locator->expects($this->once()) ->method('has') ->with('\App\Processor') ->willReturn(true); $this->locator->expects($this->once()) ->method('get') ->with('\App\Processor') ->willReturn($processor); $processor->expects($this->once()) ->method('process') ->with([], $operation, $context) ->willReturn(null); $this->processor->process([], $operation, $context); } public function testItCallsProcessMethodFromOperationProcessorAsCallable(): void { $operation = new Create(processor: [ProcessorWithCallable::class, 'process']); $context = new Context(); $result = $this->processor->process([], $operation, $context); $this->assertInstanceOf(\stdClass::class, $result); } public function testItReturnsNullIfOperationHasNoProcessor(): void { $operation = new Create(); $context = new Context(); $result = $this->processor->process([], $operation, $context); $this->assertNull($result); } public function testItThrowsExceptionWhenConfiguredProcessorIsNotAProcessorInstance(): void { $operation = new Create(processor: '\stdClass'); $context = new Context(); $this->locator->expects($this->once()) ->method('has') ->with('\stdClass') ->willReturn(true); $this->locator->expects($this->once()) ->method('get') ->with('\stdClass') ->willReturn(new \stdClass()); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Expected an instance of Sylius\Resource\State\ProcessorInterface. Got: stdClass'); $this->processor->process([], $operation, $context); } } ================================================ FILE: src/Component/tests/State/Provider/FactoryProviderTest.php ================================================ decorated = $this->createMock(ProviderInterface::class); $this->factory = $this->createMock(FactoryInterface::class); $this->factoryProvider = new FactoryProvider( $this->decorated, $this->factory, ); } /** @test */ public function it_uses_factory_from_operation(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $operation = new Create(); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn(['foo' => 'fighters']) ; $request->attributes = $attributes; $this->factory->expects($this->once()) ->method('create') ->with($operation, $context) ->willReturn($data) ; $attributes->expects($this->once()) ->method('set') ->with('data', $data) ; $this->factoryProvider->provide($operation, $context); } /** @test */ public function it_does_not_store_data_on_request_when_it_does_not_exist(): void { $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $operation = new Create(); $context = new Context(); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn(['foo' => 'fighters']) ; $this->factory->expects($this->once()) ->method('create') ->with($operation, $context) ->willReturn($data) ; $attributes->expects($this->never())->method('set'); $this->factoryProvider->provide($operation, $context); } /** @test */ public function it_does_nothing_when_operation_is_not_a_factory_aware_operation(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $operation = new Update(); $context = new Context(); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn(['foo' => 'fighters']) ; $request->attributes = $attributes; $this->factory->expects($this->never())->method('create'); $attributes->expects($this->never())->method('set'); $this->factoryProvider->provide($operation, $context); } /** @test */ public function it_does_nothing_when_factory_is_disabled(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $data = $this->createMock(\stdClass::class); $operation = new Create(factory: false); $context = new Context(); $this->decorated->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn(['foo' => 'fighters']) ; $request->attributes = $attributes; $this->factory->expects($this->never())->method('create'); $attributes->expects($this->never())->method('set'); $this->factoryProvider->provide($operation, $context); } } ================================================ FILE: src/Component/tests/State/Provider/ReadProviderTest.php ================================================ provider = $this->createMock(ProviderInterface::class); $this->readProvider = new ReadProvider( $this->provider, ); } /** @test */ public function it_retrieves_data(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(); $request->attributes = $attributes; $this->provider->expects($this->once())->method('provide')->with($operation, $this->isInstanceOf(Context::class))->willReturn(['foo' => 'fighters']); $attributes->expects($this->never())->method('set'); $this->readProvider->provide($operation, $context); } /** @test */ public function it_retrieves_data_and_store_them_to_request(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(new RequestOption($request)); $request->attributes = $attributes; $this->provider->expects($this->once())->method('provide')->with($operation, $this->isInstanceOf(Context::class))->willReturn(['foo' => 'fighters']); $attributes->expects($this->once())->method('set')->with('data', ['foo' => 'fighters']); $this->readProvider->provide($operation, $context); } /** @test */ public function it_does_nothing_when_operation_is_a_create_operation(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $operation = new Create(); $context = new Context(new RequestOption($request)); $request->attributes = $attributes; $this->provider->expects($this->never())->method('provide'); $attributes->expects($this->never())->method('set'); $this->readProvider->provide($operation, $context); } /** @test */ public function it_does_nothing_when_operation_cannot_be_read(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(new RequestOption($request)); $request->attributes = $attributes; $operation->method('canRead')->willReturn(false); $this->provider->expects($this->never())->method('provide'); $attributes->expects($this->never())->method('set'); $this->readProvider->provide($operation, $context); } /** @test */ public function it_throws_an_exception_when_no_data_was_found(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(new RequestOption($request)); $request->attributes = $attributes; $operation->method('canRead')->willReturn(true); $this->provider->expects($this->once())->method('provide')->with($operation, $this->isInstanceOf(Context::class))->willReturn(null); $attributes->expects($this->never())->method('set'); $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage('Resource has not been found.'); $this->readProvider->provide($operation, $context); } } ================================================ FILE: src/Component/tests/State/ProviderTest.php ================================================ locator = $this->createMock(ContainerInterface::class); $this->provider = new Provider($this->locator); } public function testItIsInitializable(): void { $this->assertInstanceOf(Provider::class, $this->provider); } public function testItCallsProvideMethodFromOperationProviderAsString(): void { $operation = new Create(provider: '\App\Provider'); $context = new Context(); $provider = $this->createMock(ProviderInterface::class); $this->locator->expects($this->once()) ->method('has') ->with('\App\Provider') ->willReturn(true); $this->locator->expects($this->once()) ->method('get') ->with('\App\Provider') ->willReturn($provider); $provider->expects($this->once()) ->method('provide') ->with($operation, $context); $this->provider->provide($operation, $context); } public function testItCallsProvideMethodFromOperationProviderAsCallable(): void { $operation = new Create(provider: [ProviderWithCallable::class, 'provide']); $context = new Context(); $result = $this->provider->provide($operation, $context); $this->assertInstanceOf(\stdClass::class, $result); } public function testItReturnsNullIfOperationHasNoProvider(): void { $operation = new Create(); $context = new Context(); $result = $this->provider->provide($operation, $context); $this->assertNull($result); } public function testItThrowsExceptionWhenConfiguredProviderIsNotAProviderInstance(): void { $operation = new Create(provider: '\stdClass'); $context = new Context(); $this->locator->expects($this->once()) ->method('has') ->with('\stdClass') ->willReturn(true); $this->locator->expects($this->once()) ->method('get') ->with('\stdClass') ->willReturn(new \stdClass()); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Expected an instance of Sylius\Resource\State\ProviderInterface. Got: stdClass'); $this->provider->provide($operation, $context); } } ================================================ FILE: src/Component/tests/State/ResponderTest.php ================================================ locator = $this->createMock(ContainerInterface::class); $this->responder = new Responder($this->locator); } public function testItIsInitializable(): void { $this->assertInstanceOf(Responder::class, $this->responder); } public function testItCallsRespondMethodFromOperationResponderAsString(): void { $operation = new Create(responder: '\App\Responder'); $context = new Context(); $responder = $this->createMock(ResponderInterface::class); $this->locator->expects($this->once()) ->method('has') ->with('\App\Responder') ->willReturn(true); $this->locator->expects($this->once()) ->method('get') ->with('\App\Responder') ->willReturn($responder); $responder->expects($this->once()) ->method('respond') ->with([], $operation, $context) ->willReturn('response_data'); $result = $this->responder->respond([], $operation, $context); $this->assertEquals('response_data', $result); } public function testItCallsRespondMethodFromOperationResponderAsCallable(): void { $operation = new Create(responder: [ResponderWithCallable::class, 'respond']); $context = new Context(); $result = $this->responder->respond([], $operation, $context); $this->assertInstanceOf(Response::class, $result); } public function testItReturnsNullIfOperationHasNoResponder(): void { $operation = new Create(); $context = new Context(); $result = $this->responder->respond([], $operation, $context); $this->assertNull($result); } public function testItThrowsExceptionWhenConfiguredResponderIsNotAResponderInstance(): void { $operation = new Create(responder: '\stdClass'); $context = new Context(); $this->locator->expects($this->once()) ->method('has') ->with('\stdClass') ->willReturn(true); $this->locator->expects($this->once()) ->method('get') ->with('\stdClass') ->willReturn(new \stdClass()); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Expected an instance of Sylius\Resource\State\ResponderInterface. Got: stdClass'); $this->responder->respond([], $operation, $context); } } ================================================ FILE: src/Component/tests/StateMachine/OperationStateMachineTest.php ================================================ locator = $this->createMock(ContainerInterface::class); $this->operationStateMachine = new OperationStateMachine($this->locator); } public function testItIsInitializable(): void { $this->assertInstanceOf(OperationStateMachine::class, $this->operationStateMachine); } public function testItCallsCanMethodFromOperationStateMachineAsString(): void { $stateMachine = $this->createMock(OperationStateMachineInterface::class); $data = new \stdClass(); $operation = (new Create())->withStateMachineComponent('symfony'); $context = new Context(); $this->locator->expects($this->once()) ->method('has') ->with('symfony') ->willReturn(true); $this->locator->expects($this->once()) ->method('get') ->with('symfony') ->willReturn($stateMachine); $stateMachine->expects($this->once()) ->method('can') ->with($data, $operation, $context) ->willReturn(true); $this->assertTrue($this->operationStateMachine->can($data, $operation, $context)); } public function testItReturnsFalseIfNoOperationStateMachineHasBeenConfiguredOnOperation(): void { $data = new \stdClass(); $operation = new Create(); $context = new Context(); $this->assertFalse($this->operationStateMachine->can($data, $operation, $context)); } public function testItCallsApplyMethodFromOperationStateMachineAsString(): void { $stateMachine = $this->createMock(OperationStateMachineInterface::class); $data = new \stdClass(); $operation = (new Create())->withStateMachineComponent('symfony'); $context = new Context(); $this->locator->expects($this->once()) ->method('has') ->with('symfony') ->willReturn(true); $this->locator->expects($this->once()) ->method('get') ->with('symfony') ->willReturn($stateMachine); $stateMachine->expects($this->once()) ->method('apply') ->with($data, $operation, $context); $this->operationStateMachine->apply($data, $operation, $context); } public function testItDoesNothingIfNoOperationStateMachineHasBeenConfiguredOnOperation(): void { $data = new \stdClass(); $operation = new Create(); $context = new Context(); $this->operationStateMachine->apply($data, $operation, $context); $this->expectNotToPerformAssertions(); } public function testItThrowsAnExceptionWhenOperationDoesNotImplementAStateMachine(): void { $data = new \stdClass(); $operation = new Index(); $this->expectException(\LogicException::class); $this->expectExceptionMessage(sprintf('Expected an instance of %s. Got: %s', StateMachineAwareOperationInterface::class, Index::class)); $this->operationStateMachine->can($data, $operation, new Context()); } } ================================================ FILE: src/Component/tests/StateMachine/State/ApplyStateMachineTransitionProcessorTest.php ================================================ operationStateMachine = $this->createMock(OperationStateMachineInterface::class); $this->writeProcessor = $this->createMock(ProcessorInterface::class); $this->processor = new ApplyStateMachineTransitionProcessor( $this->operationStateMachine, $this->writeProcessor, ); } public function testItIsInitializable(): void { $this->assertInstanceOf(ApplyStateMachineTransitionProcessor::class, $this->processor); } public function testItAppliesStateMachineTransitionIfPossible(): void { $data = new \stdClass(); $operation = new Create(); $context = new Context(); $this->operationStateMachine->expects($this->once()) ->method('can') ->with($data, $operation, $context) ->willReturn(true); $this->operationStateMachine->expects($this->once()) ->method('apply') ->with($data, $operation, $context); $this->writeProcessor->expects($this->once()) ->method('process') ->with($data, $operation, $context) ->willReturn(null); $this->processor->process($data, $operation, $context); } public function testItDoesNothingWhenTransitionIsNotPossible(): void { $data = new \stdClass(); $operation = new Create(); $context = new Context(); $this->operationStateMachine->expects($this->once()) ->method('can') ->with($data, $operation, $context) ->willReturn(false); $this->operationStateMachine->expects($this->never()) ->method('apply'); $this->writeProcessor->expects($this->once()) ->method('process') ->with($data, $operation, $context) ->willReturn(null); $this->assertNull($this->processor->process($data, $operation, $context)); } } ================================================ FILE: src/Component/tests/StateMachine/StateMachineTest.php ================================================ markAsSkippedIfWinzouStateMachineIsNotAvailable(); $this->stateMachine = new StateMachine(new PullRequest(), [ 'graph' => 'pull_request', 'property_path' => 'currentPlace', 'places' => [ 'start', 'test', ], 'transitions' => [ 'submit' => [ 'from' => ['start'], 'to' => 'test', ], ], ]); } public function testItIsInitializable(): void { $this->assertInstanceOf(StateMachine::class, $this->stateMachine); } public function testItGetsTransitionFromAState(): void { $this->assertSame('submit', $this->stateMachine->getTransitionFromState('start')); } public function testItGetsTransitionToAState(): void { $this->assertSame('submit', $this->stateMachine->getTransitionToState('test')); } private function markAsSkippedIfWinzouStateMachineIsNotAvailable(): void { if (!class_exists(winzouStateMachineBundle::class)) { $this->markTestSkipped('Winzou State machine is not available.'); } } } ================================================ FILE: src/Component/tests/Symfony/Controller/MainControllerTest.php ================================================ operationInitiator = $this->createMock(HttpOperationInitiatorInterface::class); $this->requestContextInitiator = $this->createMock(RequestContextInitiatorInterface::class); $this->provider = $this->createMock(ProviderInterface::class); $this->processor = $this->createMock(ProcessorInterface::class); $this->mainController = new MainController( $this->operationInitiator, $this->requestContextInitiator, $this->provider, $this->processor, ); } /** @test */ public function it_returns_a_response(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $operation = new Create(); $context = new Context(); $response = new Response(); $data = new \stdClass(); $request->attributes = $attributes; $this->operationInitiator->expects($this->once())->method('initializeOperation')->with($request)->willReturn($operation); $this->requestContextInitiator->expects($this->once())->method('initializeContext')->with($request)->willReturn($context); $request->method('isMethodSafe')->willReturn(true); $attributes->expects($this->once())->method('getBoolean')->with('is_valid', true)->willReturn(true); $this->provider->expects($this->once())->method('provide')->with($operation, $context)->willReturn($data); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($response); $result = $this->mainController->__invoke($request); $this->assertEquals($response, $result); } /** @test */ public function it_throws_an_exception_when_operation_is_null(): void { $request = $this->createMock(Request::class); $this->operationInitiator->expects($this->once())->method('initializeOperation')->with($request)->willReturn(null); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Operation should not be null.'); $this->mainController->__invoke($request); } /** @test */ public function it_disables_write_if_http_method_is_safe(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(); $response = new Response(); $data = new \stdClass(); $request->attributes = $attributes; $this->operationInitiator->expects($this->once())->method('initializeOperation')->with($request)->willReturn($operation); $this->requestContextInitiator->expects($this->once())->method('initializeContext')->with($request)->willReturn($context); $request->method('isMethodSafe')->willReturn(true); $attributes->expects($this->once())->method('getBoolean')->with('is_valid', true)->willReturn(true); $this->provider->expects($this->once())->method('provide')->with($operation, $context)->willReturn($data); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($response); $operation->method('canWrite')->willReturn(null); $operation->expects($this->once())->method('withWrite')->with(false)->willReturn($operation); $this->mainController->__invoke($request); } /** @test */ public function it_disables_write_if_validation_has_failed(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(); $response = new Response(); $data = new \stdClass(); $request->attributes = $attributes; $this->operationInitiator->expects($this->once())->method('initializeOperation')->with($request)->willReturn($operation); $this->requestContextInitiator->expects($this->once())->method('initializeContext')->with($request)->willReturn($context); $attributes->expects($this->once())->method('getBoolean')->with('is_valid', true)->willReturn(false); $this->provider->expects($this->once())->method('provide')->with($operation, $context)->willReturn($data); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($response); $operation->method('canWrite')->willReturn(true); $operation->expects($this->once())->method('withWrite')->with(false)->willReturn($operation); $this->mainController->__invoke($request); } /** @test */ public function it_does_not_enable_write_if_validation_is_ok(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(); $response = new Response(); $data = new \stdClass(); $request->attributes = $attributes; $this->operationInitiator->expects($this->once())->method('initializeOperation')->with($request)->willReturn($operation); $this->requestContextInitiator->expects($this->once())->method('initializeContext')->with($request)->willReturn($context); $attributes->expects($this->once())->method('getBoolean')->with('is_valid', true)->willReturn(true); $this->provider->expects($this->once())->method('provide')->with($operation, $context)->willReturn($data); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($response); $operation->method('canWrite')->willReturn(false); $operation->expects($this->never())->method('withWrite'); $this->mainController->__invoke($request); } } ================================================ FILE: src/Component/tests/Symfony/DependencyInjection/Compiler/DisableMetadataCachePassTest.php ================================================ setDefinition($cachedServiceId, new Definition()); $this->setParameter('kernel.debug', true); $this->compile(); $this->assertContainerBuilderNotHasService($cachedServiceId); } #[Test] #[DataProvider('getCachedServiceIdProvider')] public function it_does_not_disable_cache_when_debug_is_disabled(string $cachedServiceId): void { $this->setDefinition($cachedServiceId, new Definition()); $this->setParameter('kernel.debug', false); $this->compile(); $this->assertContainerBuilderHasService($cachedServiceId); } #[Test] #[DataProvider('getCachedServiceIdProvider')] public function it_does_not_disable_cache_when_debug_parameter_does_not_exist(string $cachedServiceId): void { $this->setDefinition('sylius.resource_metadata_collection.factory.cached', new Definition()); $this->compile(); $this->assertContainerBuilderHasService('sylius.resource_metadata_collection.factory.cached'); } protected function registerCompilerPass(ContainerBuilder $container): void { $container->addCompilerPass(new DisableMetadataCachePass()); } public static function getCachedServiceIdProvider(): iterable { yield ['sylius.resource_metadata_collection.factory.cached']; yield ['sylius.metadata.resource_class_list.cached']; } } ================================================ FILE: src/Component/tests/Symfony/DependencyInjection/Compiler/FallbackToKernelDefaultLocalePassTest.php ================================================ setParameter('locale', 'en_US'); $this->setDefinition('sylius.resource_controller.flash_helper', new Definition()); $this->compile(); $this->assertContainerBuilderHasService('sylius.resource_controller.flash_helper'); } /** @test */ public function it_replaces_locale_argument_in_flash_helper(): void { $flashHelperDefinition = (new Definition(FlashHelper::class))->setArguments([ null, null, null, ]); $this->setParameter('kernel.default_locale', 'en_US'); $this->setDefinition('sylius.resource_controller.flash_helper', $flashHelperDefinition); $this->compile(); $this->assertContainerBuilderHasService('sylius.resource_controller.flash_helper'); $this->assertContainerBuilderHasServiceDefinitionWithArgument('sylius.resource_controller.flash_helper', 2, 'en_US'); } /** @test */ public function it_replaces_locale_argument_in_translation_locale_provider(): void { $translationLocaleProviderDefinition = (new Definition(FlashHelper::class))->setArguments([ [], null, ]); $this->setParameter('kernel.default_locale', 'en_US'); $this->setDefinition('sylius.translation_locale_provider.immutable', $translationLocaleProviderDefinition); $this->compile(); $this->assertContainerBuilderHasService('sylius.translation_locale_provider.immutable'); $this->assertContainerBuilderHasServiceDefinitionWithArgument('sylius.translation_locale_provider.immutable', 0, ['en_US']); $this->assertContainerBuilderHasServiceDefinitionWithArgument('sylius.translation_locale_provider.immutable', 1, 'en_US'); } protected function registerCompilerPass(ContainerBuilder $container): void { $container->addCompilerPass(new FallbackToKernelDefaultLocalePass()); } } ================================================ FILE: src/Component/tests/Symfony/DependencyInjection/Compiler/MetadataMutatorPassTest.php ================================================ compile(); $this->assertContainerBuilderNotHasService('sylius.metadata.mutator_collection.resource'); } public function testItDoesNothingWhenOperationMutatorCollectionDoesNotExist(): void { $this->compile(); $this->assertContainerBuilderNotHasService('sylius.metadata.mutator_collection.operation'); } public function testItAddsResourceMutatorsToCollection(): void { $mutatorCollectionDefinition = new Definition(); $this->setDefinition('sylius.metadata.mutator_collection.resource', $mutatorCollectionDefinition); $mutatorDefinition = new Definition(); $mutatorDefinition->addTag('sylius.resource_mutator', ['resourceClass' => 'App\Entity\Product']); $this->setDefinition('app.resource_mutator.product', $mutatorDefinition); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.resource', 'add', [ 'App\Entity\Product', new Reference('app.resource_mutator.product'), ], ); } public function testItAddsMultipleResourceMutatorsToCollection(): void { $mutatorCollectionDefinition = new Definition(); $this->setDefinition('sylius.metadata.mutator_collection.resource', $mutatorCollectionDefinition); $productMutatorDefinition = new Definition(); $productMutatorDefinition->addTag('sylius.resource_mutator', ['resourceClass' => 'App\Entity\Product']); $this->setDefinition('app.resource_mutator.product', $productMutatorDefinition); $customerMutatorDefinition = new Definition(); $customerMutatorDefinition->addTag('sylius.resource_mutator', ['resourceClass' => 'App\Entity\Customer']); $this->setDefinition('app.resource_mutator.customer', $customerMutatorDefinition); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.resource', 'add', [ 'App\Entity\Product', new Reference('app.resource_mutator.product'), ], ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.resource', 'add', [ 'App\Entity\Customer', new Reference('app.resource_mutator.customer'), ], ); } public function testItAddsResourceMutatorWithMultipleTags(): void { $mutatorCollectionDefinition = new Definition(); $this->setDefinition('sylius.metadata.mutator_collection.resource', $mutatorCollectionDefinition); $mutatorDefinition = new Definition(); $mutatorDefinition->addTag('sylius.resource_mutator', ['resourceClass' => 'App\Entity\Product']); $mutatorDefinition->addTag('sylius.resource_mutator', ['resourceClass' => 'App\Entity\Customer']); $this->setDefinition('app.resource_mutator.multi', $mutatorDefinition); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.resource', 'add', [ 'App\Entity\Product', new Reference('app.resource_mutator.multi'), ], ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.resource', 'add', [ 'App\Entity\Customer', new Reference('app.resource_mutator.multi'), ], ); } public function testItAddsOperationMutatorsToCollection(): void { $mutatorCollectionDefinition = new Definition(); $this->setDefinition('sylius.metadata.mutator_collection.operation', $mutatorCollectionDefinition); $mutatorDefinition = new Definition(); $mutatorDefinition->addTag('sylius.operation_mutator', ['operationName' => 'app_product_create']); $this->setDefinition('app.operation_mutator.product_create', $mutatorDefinition); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.operation', 'add', [ 'app_product_create', new Reference('app.operation_mutator.product_create'), ], ); } public function testItAddsMultipleOperationMutatorsToCollection(): void { $mutatorCollectionDefinition = new Definition(); $this->setDefinition('sylius.metadata.mutator_collection.operation', $mutatorCollectionDefinition); $createMutatorDefinition = new Definition(); $createMutatorDefinition->addTag('sylius.operation_mutator', ['operationName' => 'app_product_create']); $this->setDefinition('app.operation_mutator.product_create', $createMutatorDefinition); $updateMutatorDefinition = new Definition(); $updateMutatorDefinition->addTag('sylius.operation_mutator', ['operationName' => 'app_product_update']); $this->setDefinition('app.operation_mutator.product_update', $updateMutatorDefinition); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.operation', 'add', [ 'app_product_create', new Reference('app.operation_mutator.product_create'), ], ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.operation', 'add', [ 'app_product_update', new Reference('app.operation_mutator.product_update'), ], ); } public function testItAddsOperationMutatorWithMultipleTags(): void { $mutatorCollectionDefinition = new Definition(); $this->setDefinition('sylius.metadata.mutator_collection.operation', $mutatorCollectionDefinition); $mutatorDefinition = new Definition(); $mutatorDefinition->addTag('sylius.operation_mutator', ['operationName' => 'app_product_create']); $mutatorDefinition->addTag('sylius.operation_mutator', ['operationName' => 'app_product_update']); $this->setDefinition('app.operation_mutator.multi', $mutatorDefinition); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.operation', 'add', [ 'app_product_create', new Reference('app.operation_mutator.multi'), ], ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.operation', 'add', [ 'app_product_update', new Reference('app.operation_mutator.multi'), ], ); } public function testItProcessesBothResourceAndOperationMutators(): void { $resourceMutatorCollectionDefinition = new Definition(); $this->setDefinition('sylius.metadata.mutator_collection.resource', $resourceMutatorCollectionDefinition); $operationMutatorCollectionDefinition = new Definition(); $this->setDefinition('sylius.metadata.mutator_collection.operation', $operationMutatorCollectionDefinition); $resourceMutatorDefinition = new Definition(); $resourceMutatorDefinition->addTag('sylius.resource_mutator', ['resourceClass' => 'App\Entity\Product']); $this->setDefinition('app.resource_mutator', $resourceMutatorDefinition); $operationMutatorDefinition = new Definition(); $operationMutatorDefinition->addTag('sylius.operation_mutator', ['operationName' => 'app_product_create']); $this->setDefinition('app.operation_mutator', $operationMutatorDefinition); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.resource', 'add', [ 'App\Entity\Product', new Reference('app.resource_mutator'), ], ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.metadata.mutator_collection.operation', 'add', [ 'app_product_create', new Reference('app.operation_mutator'), ], ); } protected function registerCompilerPass(ContainerBuilder $container): void { $container->addCompilerPass(new MetadataMutatorPass()); } } ================================================ FILE: src/Component/tests/Symfony/EventDispatcher/GenericEventTest.php ================================================ assertInstanceOf(GenericEvent::class, $event); } public function testItCanSetAndGetMessageType(): void { $event = new GenericEvent(); $event->setMessageType(GenericEvent::TYPE_SUCCESS); $this->assertSame(GenericEvent::TYPE_SUCCESS, $event->getMessageType()); } public function testItCanSetAndGetMessage(): void { $event = new GenericEvent(); $event->setMessage('Test message'); $this->assertSame('Test message', $event->getMessage()); } public function testItCanSetAndGetMessageParameters(): void { $event = new GenericEvent(); $parameters = ['%name%' => 'John', '%count%' => 5]; $event->setMessageParameters($parameters); $this->assertSame($parameters, $event->getMessageParameters()); } public function testItCanSetAndGetErrorCode(): void { $event = new GenericEvent(); $event->setErrorCode(404); $this->assertSame(404, $event->getErrorCode()); } public function testItCanSetAndCheckResponse(): void { $event = new GenericEvent(); $response = new Response(); $this->assertFalse($event->hasResponse()); $event->setResponse($response); $this->assertTrue($event->hasResponse()); $this->assertSame($response, $event->getResponse()); } public function testItReturnsNullWhenNoResponse(): void { $event = new GenericEvent(); $this->assertNull($event->getResponse()); } public function testItCanStopPropagationWithDefaultValues(): void { $event = new GenericEvent(); $event->stop('Error occurred'); $this->assertTrue($event->isStopped()); $this->assertSame('Error occurred', $event->getMessage()); $this->assertSame(GenericEvent::TYPE_ERROR, $event->getMessageType()); $this->assertSame([], $event->getMessageParameters()); $this->assertSame(500, $event->getErrorCode()); } public function testItCanStopPropagationWithCustomType(): void { $event = new GenericEvent(); $event->stop('Warning message', GenericEvent::TYPE_WARNING); $this->assertTrue($event->isStopped()); $this->assertSame('Warning message', $event->getMessage()); $this->assertSame(GenericEvent::TYPE_WARNING, $event->getMessageType()); $this->assertSame(500, $event->getErrorCode()); } public function testItCanStopPropagationWithCustomParameters(): void { $event = new GenericEvent(); $parameters = ['%item%' => 'Product']; $event->stop('Item not found', GenericEvent::TYPE_ERROR, $parameters); $this->assertTrue($event->isStopped()); $this->assertSame('Item not found', $event->getMessage()); $this->assertSame(GenericEvent::TYPE_ERROR, $event->getMessageType()); $this->assertSame($parameters, $event->getMessageParameters()); $this->assertSame(500, $event->getErrorCode()); } public function testItCanStopPropagationWithCustomErrorCode(): void { $event = new GenericEvent(); $event->stop('Not found', GenericEvent::TYPE_ERROR, [], 404); $this->assertTrue($event->isStopped()); $this->assertSame('Not found', $event->getMessage()); $this->assertSame(GenericEvent::TYPE_ERROR, $event->getMessageType()); $this->assertSame([], $event->getMessageParameters()); $this->assertSame(404, $event->getErrorCode()); } public function testItCanStopPropagationWithAllParameters(): void { $event = new GenericEvent(); $parameters = ['%name%' => 'User', '%id%' => 123]; $event->stop('User not found', GenericEvent::TYPE_WARNING, $parameters, 404); $this->assertTrue($event->isStopped()); $this->assertSame('User not found', $event->getMessage()); $this->assertSame(GenericEvent::TYPE_WARNING, $event->getMessageType()); $this->assertSame($parameters, $event->getMessageParameters()); $this->assertSame(404, $event->getErrorCode()); } public function testItHasTypeErrorConstant(): void { $this->assertSame('error', GenericEvent::TYPE_ERROR); } public function testItHasTypeWarningConstant(): void { $this->assertSame('warning', GenericEvent::TYPE_WARNING); } public function testItHasTypeInfoConstant(): void { $this->assertSame('info', GenericEvent::TYPE_INFO); } public function testItHasTypeSuccessConstant(): void { $this->assertSame('success', GenericEvent::TYPE_SUCCESS); } public function testItCanBeCreatedWithSubject(): void { $subject = new \stdClass(); $event = new GenericEvent($subject); $this->assertSame($subject, $event->getSubject()); } public function testItCanBeCreatedWithSubjectAndArguments(): void { $subject = new \stdClass(); $arguments = ['key1' => 'value1', 'key2' => 'value2']; $event = new GenericEvent($subject, $arguments); $this->assertSame($subject, $event->getSubject()); $this->assertSame('value1', $event->getArgument('key1')); $this->assertSame('value2', $event->getArgument('key2')); } public function testItReturnsEmptyStringForMessageTypeByDefault(): void { $event = new GenericEvent(); $this->assertSame('', $event->getMessageType()); } public function testItReturnsEmptyStringForMessageByDefault(): void { $event = new GenericEvent(); $this->assertSame('', $event->getMessage()); } public function testItReturnsEmptyArrayForMessageParametersByDefault(): void { $event = new GenericEvent(); $this->assertSame([], $event->getMessageParameters()); } public function testItReturns500ForErrorCodeByDefault(): void { $event = new GenericEvent(); $this->assertSame(500, $event->getErrorCode()); } public function testItIsNotStoppedByDefault(): void { $event = new GenericEvent(); $this->assertFalse($event->isStopped()); } } ================================================ FILE: src/Component/tests/Symfony/EventDispatcher/OperationEventDispatcherTest.php ================================================ eventDispatcher = $this->createMock(EventDispatcherInterface::class); $this->operationEventDispatcher = new OperationEventDispatcher($this->eventDispatcher); } public function testItIsInitializable(): void { $this->assertInstanceOf(OperationEventDispatcher::class, $this->operationEventDispatcher); } public function testItDispatchesEvents(): void { $data = new \stdClass(); $resource = $this->createResource(); $show = (new Show(eventShortName: 'read'))->withResource($resource); $context = new Context(); $this->eventDispatcher ->expects($this->once()) ->method('dispatch') ->with( $this->callback(fn (OperationEvent $event) => $event->getSubject() === $data), 'app.book.read', ) ->willReturnArgument(0); $result = $this->operationEventDispatcher->dispatch($data, $show, $context); $this->assertInstanceOf(OperationEvent::class, $result); } public function testItDispatchesEventsForBulkOperations(): void { $data = new \ArrayObject(); $resource = $this->createResource(); $bulkDelete = (new BulkDelete(eventShortName: 'delete'))->withResource($resource); $context = new Context(); $this->eventDispatcher ->expects($this->once()) ->method('dispatch') ->with( $this->callback(fn (OperationEvent $event) => $event->getSubject() === $data), 'app.book.bulk_delete', ) ->willReturnArgument(0); $result = $this->operationEventDispatcher->dispatchBulkEvent($data, $bulkDelete, $context); $this->assertInstanceOf(OperationEvent::class, $result); } public function testItDispatchesPreEvents(): void { $data = new \stdClass(); $resource = $this->createResource(); $create = (new Create(eventShortName: 'create'))->withResource($resource); $context = new Context(); $this->eventDispatcher ->expects($this->once()) ->method('dispatch') ->with( $this->callback(fn (OperationEvent $event) => $event->getSubject() === $data), 'app.book.pre_create', ) ->willReturnArgument(0); $result = $this->operationEventDispatcher->dispatchPreEvent($data, $create, $context); $this->assertInstanceOf(OperationEvent::class, $result); } public function testItDispatchesPostEvents(): void { $data = new \stdClass(); $resource = $this->createResource(); $create = (new Create(eventShortName: 'create'))->withResource($resource); $context = new Context(); $this->eventDispatcher ->expects($this->once()) ->method('dispatch') ->with( $this->callback(fn (OperationEvent $event) => $event->getSubject() === $data), 'app.book.post_create', ) ->willReturnArgument(0); $result = $this->operationEventDispatcher->dispatchPostEvent($data, $create, $context); $this->assertInstanceOf(OperationEvent::class, $result); } public function testItDispatchesInitializeEvents(): void { $data = new \stdClass(); $resource = $this->createResource(); $create = (new Create(eventShortName: 'create'))->withResource($resource); $context = new Context(); $this->eventDispatcher ->expects($this->once()) ->method('dispatch') ->with( $this->callback(fn (OperationEvent $event) => $event->getSubject() === $data), 'app.book.initialize_create', ) ->willReturnArgument(0); $result = $this->operationEventDispatcher->dispatchInitializeEvent($data, $create, $context); $this->assertInstanceOf(OperationEvent::class, $result); } public function testItReturnsEventWithoutDispatchingWhenResourceIsNull(): void { $data = new \stdClass(); $create = new Create(); $context = new Context(); $this->eventDispatcher ->expects($this->never()) ->method('dispatch'); $result = $this->operationEventDispatcher->dispatch($data, $create, $context); $this->assertInstanceOf(OperationEvent::class, $result); $this->assertSame($data, $result->getSubject()); } public function testItReturnsEventWithoutDispatchingWhenResourceIsNullForBulkEvent(): void { $data = new \ArrayObject(); $bulkDelete = new BulkDelete(); $context = new Context(); $this->eventDispatcher ->expects($this->never()) ->method('dispatch'); $result = $this->operationEventDispatcher->dispatchBulkEvent($data, $bulkDelete, $context); $this->assertInstanceOf(OperationEvent::class, $result); $this->assertSame($data, $result->getSubject()); } public function testItReturnsEventWithoutDispatchingWhenResourceIsNullForPreEvent(): void { $data = new \stdClass(); $create = new Create(); $context = new Context(); $this->eventDispatcher ->expects($this->never()) ->method('dispatch'); $result = $this->operationEventDispatcher->dispatchPreEvent($data, $create, $context); $this->assertInstanceOf(OperationEvent::class, $result); $this->assertSame($data, $result->getSubject()); } public function testItReturnsEventWithoutDispatchingWhenResourceIsNullForPostEvent(): void { $data = new \stdClass(); $create = new Create(); $context = new Context(); $this->eventDispatcher ->expects($this->never()) ->method('dispatch'); $result = $this->operationEventDispatcher->dispatchPostEvent($data, $create, $context); $this->assertInstanceOf(OperationEvent::class, $result); $this->assertSame($data, $result->getSubject()); } public function testItReturnsEventWithoutDispatchingWhenResourceIsNullForInitializeEvent(): void { $data = new \stdClass(); $create = new Create(); $context = new Context(); $this->eventDispatcher ->expects($this->never()) ->method('dispatch'); $result = $this->operationEventDispatcher->dispatchInitializeEvent($data, $create, $context); $this->assertInstanceOf(OperationEvent::class, $result); $this->assertSame($data, $result->getSubject()); } private function createResource(): ResourceMetadata { return new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); } } ================================================ FILE: src/Component/tests/Symfony/EventDispatcher/OperationEventHandlerTest.php ================================================ redirectHandler = $this->createMock(RedirectHandlerInterface::class); $this->flashHelper = $this->createMock(FlashHelperInterface::class); $this->operationEventHandler = new OperationEventHandler($this->redirectHandler, $this->flashHelper); } public function testItIsInitializable(): void { $this->assertInstanceOf(OperationEventHandler::class, $this->operationEventHandler); } public function testItReturnsNullWhenPreProcessEventIsNotStopped(): void { $event = new OperationEvent(); $context = new Context(); $this->flashHelper->expects($this->never())->method('addFlashFromEvent'); $this->redirectHandler->expects($this->never())->method('redirectToResource'); $this->redirectHandler->expects($this->never())->method('redirectToOperation'); $result = $this->operationEventHandler->handlePreProcessEvent($event, $context); $this->assertNull($result); } public function testItThrowsAnHttpExceptionWhenPreProcessEventIsStoppedAndRequestFormatIsNotHtml(): void { $event = new OperationEvent(); $event->stop(message: 'What the hell is going on?', errorCode: 666); $context = new Context(); try { $this->operationEventHandler->handlePreProcessEvent($event, $context); $this->fail('Expected HttpException to be thrown'); } catch (HttpException $e) { $this->assertSame('What the hell is going on?', $e->getMessage()); $this->assertSame(666, $e->getStatusCode()); } } public function testItReturnsResponseFromPreProcessEventWhenItHasOneAndRequestFormatIsHtml(): void { $response = $this->createMock(Response::class); $request = $this->createMock(Request::class); $event = new OperationEvent(); $event->stop(message: 'What the hell is going on?', errorCode: 666); $event->setResponse($response); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('html'); $this->flashHelper->expects($this->once())->method('addFlashFromEvent')->with($event, $context); $result = $this->operationEventHandler->handlePreProcessEvent($event, $context); $this->assertSame($response, $result); } public function testItDoesNotReturnsResponseFromPreProcessEventWhenRequestFormatIsNotHtml(): void { $response = $this->createMock(Response::class); $request = $this->createMock(Request::class); $event = new OperationEvent(); $event->stop(message: 'What the hell is going on?', errorCode: 666); $event->setResponse($response); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('json'); try { $this->operationEventHandler->handlePreProcessEvent($event, $context); $this->fail('Expected HttpException to be thrown'); } catch (HttpException $e) { $this->assertSame('What the hell is going on?', $e->getMessage()); $this->assertSame(666, $e->getStatusCode()); } } public function testItCanRedirectToResourceWhenPreProcessEventIsStoppedAndHasNoResponseAndOperationIsAnHttpOperation(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $redirectResponse = $this->createMock(RedirectResponse::class); $event = new OperationEvent($data); $event->stop(message: 'What the hell is going on?', errorCode: 666); $operation = new Update(); $event->setArgument('operation', $operation); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('html'); $this->flashHelper->expects($this->once())->method('addFlashFromEvent')->with($event, $context); $this->redirectHandler ->expects($this->once()) ->method('redirectToResource') ->with($data, $operation, $request) ->willReturn($redirectResponse); $result = $this->operationEventHandler->handlePreProcessEvent($event, $context); $this->assertInstanceOf(RedirectResponse::class, $result); } public function testItCanRedirectToOperationWhenPreProcessEventIsStoppedAndHasNoResponseAndOperationIsAnHttpOperation(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $redirectResponse = $this->createMock(RedirectResponse::class); $event = new OperationEvent($data); $event->stop(message: 'What the hell is going on?', errorCode: 666); $operation = new Update(); $event->setArgument('operation', $operation); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('html'); $this->flashHelper->expects($this->once())->method('addFlashFromEvent')->with($event, $context); $this->redirectHandler ->expects($this->once()) ->method('redirectToOperation') ->with($data, $operation, $request, 'index') ->willReturn($redirectResponse); $result = $this->operationEventHandler->handlePreProcessEvent($event, $context, 'index'); $this->assertInstanceOf(RedirectResponse::class, $result); } public function testItReturnsNullWhenPreProcessEventIsStoppedAndHasNoResponseAndOperationIsNotAnHttpOperation(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $operation = $this->createMock(Operation::class); $event = new OperationEvent($data); $event->stop(message: 'What the hell is going on?', errorCode: 666); $event->setArgument('operation', $operation); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('html'); $this->flashHelper->expects($this->once())->method('addFlashFromEvent')->with($event, $context); $result = $this->operationEventHandler->handlePreProcessEvent($event, $context); $this->assertNull($result); } public function testItReturnsPostProcessEventResponseWhenRequestFormatIsHtml(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $response = $this->createMock(Response::class); $event = new OperationEvent($data); $event->setResponse($response); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('html'); $result = $this->operationEventHandler->handlePostProcessEvent($event, $context); $this->assertSame($response, $result); } public function testItReturnsNullForPostProcessEventWhenRequestFormatIsHtmlButEventHasNoResponse(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $event = new OperationEvent($data); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('html'); $result = $this->operationEventHandler->handlePostProcessEvent($event, $context); $this->assertNull($result); } public function testItReturnsNullForPostProcessEventWhenRequestFormatIsNotHtml(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $event = new OperationEvent($data); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('json'); $result = $this->operationEventHandler->handlePostProcessEvent($event, $context); $this->assertNull($result); } } ================================================ FILE: src/Component/tests/Symfony/EventDispatcher/OperationEventTest.php ================================================ assertInstanceOf(OperationEvent::class, $event); } public function testItCanBeCreatedWithSubject(): void { $subject = new \stdClass(); $event = new OperationEvent($subject); $this->assertSame($subject, $event->getSubject()); } public function testItCanGetOperation(): void { $operation = new Create(); $event = new OperationEvent(); $event->setArgument('operation', $operation); $this->assertSame($operation, $event->getOperation()); } public function testItCanGetContext(): void { $context = new Context(); $event = new OperationEvent(); $event->setArgument('context', $context); $this->assertSame($context, $event->getContext()); } public function testItCanGetOperationAndContext(): void { $operation = new Create(); $context = new Context(); $subject = new \stdClass(); $event = new OperationEvent($subject); $event->setArgument('operation', $operation); $event->setArgument('context', $context); $this->assertSame($subject, $event->getSubject()); $this->assertSame($operation, $event->getOperation()); $this->assertSame($context, $event->getContext()); } } ================================================ FILE: src/Component/tests/Symfony/EventDispatcher/State/DispatchPostReadEventProviderTest.php ================================================ provider = $this->createMock(ProviderInterface::class); $this->operationEventDispatcher = $this->createMock(OperationEventDispatcherInterface::class); $this->dispatchPostReadEventProvider = new DispatchPostReadEventProvider( $this->provider, $this->operationEventDispatcher, ); } public function testItIsInitializable(): void { $this->assertInstanceOf(DispatchPostReadEventProvider::class, $this->dispatchPostReadEventProvider); } public function testItDispatchesEventsForIndexOperation(): void { $operation = new Index(provider: '\App\Provider'); $context = new Context(); $operationEvent = new OperationEvent(); $this->provider->expects($this->once())->method('provide')->with($operation, $context); $this->operationEventDispatcher ->expects($this->once()) ->method('dispatch') ->with(null, $operation, $context) ->willReturn($operationEvent); $this->dispatchPostReadEventProvider->provide($operation, $context); } public function testItDispatchesEventsForShowOperation(): void { $operation = new Show(provider: '\App\Provider'); $context = new Context(); $operationEvent = new OperationEvent(); $this->provider->expects($this->once())->method('provide')->with($operation, $context); $this->operationEventDispatcher ->expects($this->once()) ->method('dispatch') ->with(null, $operation, $context) ->willReturn($operationEvent); $this->dispatchPostReadEventProvider->provide($operation, $context); } public function testItDoesNotDispatchEventsForCreateOperation(): void { $operation = new Create(provider: '\App\Provider'); $context = new Context(); $this->provider->expects($this->once())->method('provide')->with($operation, $context); $this->operationEventDispatcher->expects($this->never())->method('dispatch'); $this->dispatchPostReadEventProvider->provide($operation, $context); } } ================================================ FILE: src/Component/tests/Symfony/EventDispatcher/State/DispatchPostWriteEventProcessorTest.php ================================================ processor = $this->createMock(ProcessorInterface::class); $this->operationEventDispatcher = $this->createMock(OperationEventDispatcherInterface::class); $this->eventHandler = $this->createMock(OperationEventHandlerInterface::class); $this->dispatchPostWriteEventProcessor = new DispatchPostWriteEventProcessor( $this->processor, $this->operationEventDispatcher, $this->eventHandler, ); } /** @test */ public function it_dispatches_post_events_with_operation_as_string(): void { $data = new \stdClass(); $operation = new Create(processor: '\App\Processor'); $context = new Context(); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($data); $postEvent = new OperationEvent(); $this->operationEventDispatcher->expects($this->once())->method('dispatchPostEvent')->with($data, $operation, $context)->willReturn($postEvent); $this->eventHandler->expects($this->once())->method('handlePostProcessEvent')->with($postEvent, $context)->willReturn(null); $result = $this->dispatchPostWriteEventProcessor->process($data, $operation, $context); $this->assertEquals($data, $result); } /** @test */ public function it_returns_post_event_response(): void { $data = new \stdClass(); $response = new Response(); $operation = new Create(processor: '\App\Processor'); $context = new Context(); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($data); $postEvent = new OperationEvent(); $this->operationEventDispatcher->expects($this->once())->method('dispatchPostEvent')->with($data, $operation, $context)->willReturn($postEvent); $this->eventHandler->expects($this->once())->method('handlePostProcessEvent')->with($postEvent, $context)->willReturn($response); $result = $this->dispatchPostWriteEventProcessor->process($data, $operation, $context); $this->assertEquals($response, $result); } /** @test */ public function it_does_nothing_if_the_decorated_processor_returns_a_response(): void { $data = new \stdClass(); $operation = new Create(processor: '\App\Processor'); $context = new Context(); $response = new Response(); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($response); $this->operationEventDispatcher->expects($this->never())->method('dispatchPostEvent'); $this->eventHandler->expects($this->never())->method('handlePostProcessEvent'); $this->dispatchPostWriteEventProcessor->process($data, $operation, $context); } } ================================================ FILE: src/Component/tests/Symfony/EventDispatcher/State/DispatchPreWriteEventProcessorTest.php ================================================ processor = $this->createMock(ProcessorInterface::class); $this->operationEventDispatcher = $this->createMock(OperationEventDispatcherInterface::class); $this->eventHandler = $this->createMock(OperationEventHandlerInterface::class); $this->dispatchPreWriteEventProcessor = new DispatchPreWriteEventProcessor( $this->processor, $this->operationEventDispatcher, $this->eventHandler, ); } /** @test */ public function it_dispatches_pre_events(): void { $data = new \stdClass(); $operation = new Create(processor: '\App\Processor'); $context = new Context(); $preEvent = new OperationEvent(); $this->operationEventDispatcher->expects($this->once())->method('dispatchPreEvent')->with($data, $operation, $context)->willReturn($preEvent); $this->eventHandler->expects($this->once())->method('handlePreProcessEvent')->with($preEvent, $context, 'index')->willReturn(null); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($data); $result = $this->dispatchPreWriteEventProcessor->process($data, $operation, $context); $this->assertEquals($data, $result); } /** @test */ public function it_does_not_call_processor_if_pre_event_returns_a_response(): void { $data = new \stdClass(); $response = new Response(); $operation = new Create(processor: '\App\Processor'); $context = new Context(); $preEvent = new OperationEvent(); $this->operationEventDispatcher->expects($this->once())->method('dispatchPreEvent')->with($data, $operation, $context)->willReturn($preEvent); $this->eventHandler->expects($this->once())->method('handlePreProcessEvent')->with($preEvent, $context, 'index')->willReturn($response); $this->processor->expects($this->never())->method('process'); $result = $this->dispatchPreWriteEventProcessor->process($data, $operation, $context); $this->assertEquals($response, $result); } } ================================================ FILE: src/Component/tests/Symfony/EventListener/AddFormatListenerTest.php ================================================ operationInitiator = $this->createMock(HttpOperationInitiatorInterface::class); $this->negotiator = new Negotiator(); $this->addFormatListener = new AddFormatListener($this->operationInitiator, $this->negotiator); } public function testItIsInitializable(): void { $this->assertInstanceOf(AddFormatListener::class, $this->addFormatListener); } public function testItDoesNothingWhenOperationIsNull(): void { $request = new Request(); $event = $this->createRequestEvent($request); $this->operationInitiator ->expects($this->once()) ->method('initializeOperation') ->with($request) ->willReturn(null); $this->addFormatListener->onKernelRequest($event); } public function testItSetsFormatFromAcceptHeaderWhenMediaTypeIsNegotiated(): void { $request = new Request(); $request->headers->set('Accept', 'application/json'); $event = $this->createRequestEvent($request); $operation = $this->createMock(HttpOperation::class); $this->operationInitiator ->method('initializeOperation') ->with($request) ->willReturn($operation); $this->addFormatListener->onKernelRequest($event); $this->assertSame('json', $request->getRequestFormat()); } public function testItSetsFormatFromAcceptHeaderForXml(): void { $request = new Request(); $request->headers->set('Accept', 'application/xml'); $event = $this->createRequestEvent($request); $operation = $this->createMock(HttpOperation::class); $this->operationInitiator ->method('initializeOperation') ->with($request) ->willReturn($operation); $this->addFormatListener->onKernelRequest($event); $this->assertSame('xml', $request->getRequestFormat()); } public function testItSetsFormatFromAcceptHeaderForHtml(): void { $request = new Request(); $request->headers->set('Accept', 'text/html'); $event = $this->createRequestEvent($request); $operation = $this->createMock(HttpOperation::class); $this->operationInitiator ->method('initializeOperation') ->with($request) ->willReturn($operation); $this->addFormatListener->onKernelRequest($event); $this->assertSame('html', $request->getRequestFormat()); } public function testItDoesNothingWhenAcceptHeaderIsNullAndRequestFormatIsNotSet(): void { $request = new Request(); $event = $this->createRequestEvent($request); $operation = $this->createMock(HttpOperation::class); $this->operationInitiator ->method('initializeOperation') ->with($request) ->willReturn($operation); $this->addFormatListener->onKernelRequest($event); $this->assertNull($request->getRequestFormat(null)); } public function testItDoesNothingWhenMediaTypeCannotBeNegotiatedAndRequestFormatIsSupported(): void { $request = new Request(); $request->headers->set('Accept', 'application/pdf'); $request->setRequestFormat('json'); $event = $this->createRequestEvent($request); $operation = $this->createMock(HttpOperation::class); $this->operationInitiator ->method('initializeOperation') ->with($request) ->willReturn($operation); $this->addFormatListener->onKernelRequest($event); $this->assertSame('json', $request->getRequestFormat()); } public function testItThrowsExceptionWhenMediaTypeCannotBeNegotiatedAndRequestFormatIsUnsupported(): void { $request = new Request(); $request->headers->set('Accept', 'application/pdf'); $request->setRequestFormat('pdf'); $event = $this->createRequestEvent($request); $operation = $this->createMock(HttpOperation::class); $this->operationInitiator ->method('initializeOperation') ->with($request) ->willReturn($operation); $this->expectException(NotAcceptableHttpException::class); $this->addFormatListener->onKernelRequest($event); } public function testItThrowsExceptionWhenRequestFormatHasNoMimeType(): void { $request = new Request(); $request->headers->set('Accept', 'application/unknown'); $request->setRequestFormat('unknown'); $event = $this->createRequestEvent($request); $operation = $this->createMock(HttpOperation::class); $this->operationInitiator ->method('initializeOperation') ->with($request) ->willReturn($operation); $this->expectException(NotAcceptableHttpException::class); $this->expectExceptionMessage('Requested format "" is not supported. Supported MIME types are "text/html", "application/json", "application/xml".'); $this->addFormatListener->onKernelRequest($event); } public function testItHandlesComplexAcceptHeaderWithMultipleTypes(): void { $request = new Request(); $request->headers->set('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'); $event = $this->createRequestEvent($request); $operation = $this->createMock(HttpOperation::class); $this->operationInitiator ->method('initializeOperation') ->with($request) ->willReturn($operation); $this->addFormatListener->onKernelRequest($event); $this->assertSame('html', $request->getRequestFormat()); } public function testItDoesNotOverrideFormatWhenRequestFormatIsHtml(): void { $request = new Request(); $request->headers->set('Accept', 'application/pdf'); $request->setRequestFormat('html'); $event = $this->createRequestEvent($request); $operation = $this->createMock(HttpOperation::class); $this->operationInitiator ->method('initializeOperation') ->with($request) ->willReturn($operation); $this->addFormatListener->onKernelRequest($event); $this->assertSame('html', $request->getRequestFormat()); } private function createRequestEvent(Request $request): RequestEvent { $kernel = $this->createMock(KernelInterface::class); return new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST); } } ================================================ FILE: src/Component/tests/Symfony/ExpressionLanguage/ArgumentParserTest.php ================================================ get('sylius.expression_language.argument_parser.metadata'); $this->assertInstanceOf(ArgumentParserInterface::class, $argumentParser); $this->assertTrue($argumentParser->parseExpression('token.getUser() === null')); $this->assertTrue($argumentParser->parseExpression('user === null')); $this->assertTrue($argumentParser->parseExpression('request === null')); $this->assertTrue($argumentParser->parseExpression('throw_not_found_on_null(true)')); } public function testResourceFactoryArgumentParser(): void { self::bootKernel(); $container = static::getContainer(); /** @var ArgumentParserInterface $argumentParser */ $argumentParser = $container->get('sylius.expression_language.argument_parser.factory'); $this->assertInstanceOf(ArgumentParserInterface::class, $argumentParser); $this->assertTrue($argumentParser->parseExpression('token.getUser() === null')); $this->assertTrue($argumentParser->parseExpression('user === null')); $this->assertTrue($argumentParser->parseExpression('request === null')); $this->assertTrue($argumentParser->parseExpression('throw_not_found_on_null(true)')); } public function testRepositoryArgumentParser(): void { self::bootKernel(); $container = static::getContainer(); /** @var ArgumentParserInterface $argumentParser */ $argumentParser = $container->get('sylius.expression_language.argument_parser.repository'); $this->assertInstanceOf(ArgumentParserInterface::class, $argumentParser); $this->assertTrue($argumentParser->parseExpression('token.getUser() === null')); $this->assertTrue($argumentParser->parseExpression('user === null')); $this->assertTrue($argumentParser->parseExpression('request === null')); } public function testRoutingArgumentParser(): void { self::bootKernel(); $container = static::getContainer(); /** @var ArgumentParserInterface $argumentParser */ $argumentParser = $container->get('sylius.expression_language.argument_parser.routing'); $this->assertTrue($argumentParser->parseExpression('request === null')); $this->assertInstanceOf(ArgumentParserInterface::class, $argumentParser); } } ================================================ FILE: src/Component/tests/Symfony/ExpressionLanguage/Provider/ThrowNotFoundOnNullExpressionFunctionProviderTest.php ================================================ provider = new ThrowNotFoundOnNullExpressionFunctionProvider(); $this->expressionLanguage = new ExpressionLanguage(); $this->expressionLanguage->registerProvider($this->provider); } public function testItIsInitializable(): void { $this->assertInstanceOf(ThrowNotFoundOnNullExpressionFunctionProvider::class, $this->provider); } public function testItProvidesFunctions(): void { $functions = $this->provider->getFunctions(); $this->assertIsArray($functions); $this->assertCount(1, $functions); $this->assertContainsOnlyInstancesOf(\Symfony\Component\ExpressionLanguage\ExpressionFunction::class, $functions); } public function testItReturnsTheValueWhenNotNull(): void { $result = $this->expressionLanguage->evaluate('throw_not_found_on_null(value)', [ 'value' => 'foo', ]); $this->assertSame('foo', $result); } public function testItReturnsNumericValue(): void { $result = $this->expressionLanguage->evaluate('throw_not_found_on_null(value)', [ 'value' => 123, ]); $this->assertSame(123, $result); } public function testItReturnsArrayValue(): void { $result = $this->expressionLanguage->evaluate('throw_not_found_on_null(value)', [ 'value' => ['key' => 'value'], ]); $this->assertSame(['key' => 'value'], $result); } public function testItReturnsObjectValue(): void { $object = new \stdClass(); $object->property = 'value'; $result = $this->expressionLanguage->evaluate('throw_not_found_on_null(value)', [ 'value' => $object, ]); $this->assertSame($object, $result); $this->assertSame('value', $result->property); } public function testItReturnsFalseValue(): void { $result = $this->expressionLanguage->evaluate('throw_not_found_on_null(value)', [ 'value' => false, ]); $this->assertFalse($result); } public function testItReturnsZeroValue(): void { $result = $this->expressionLanguage->evaluate('throw_not_found_on_null(value)', [ 'value' => 0, ]); $this->assertSame(0, $result); } public function testItReturnsEmptyStringValue(): void { $result = $this->expressionLanguage->evaluate('throw_not_found_on_null(value)', [ 'value' => '', ]); $this->assertSame('', $result); } public function testItThrowsNotFoundExceptionWhenNullWithEmptyMessage(): void { $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage(''); $this->expressionLanguage->evaluate('throw_not_found_on_null(value)', [ 'value' => null, ]); } public function testItThrowsNotFoundExceptionWhenNullWithCustomMessage(): void { $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage('Custom message.'); $this->expressionLanguage->evaluate('throw_not_found_on_null(value, "Custom message.")', [ 'value' => null, ]); } public function testItThrowsNotFoundExceptionWithDetailedMessage(): void { $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage('Resource with ID 123 was not found.'); $this->expressionLanguage->evaluate('throw_not_found_on_null(value, "Resource with ID 123 was not found.")', [ 'value' => null, ]); } public function testItCompilesExpressionWithoutMessage(): void { $compiled = $this->expressionLanguage->compile('throw_not_found_on_null(value)', ['value']); $this->assertIsString($compiled); $this->assertStringContainsString('null !== $value', $compiled); $this->assertStringContainsString('NotFoundHttpException()', $compiled); $this->assertSame( '(null !== $value) ? $value : throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException()', $compiled, ); } public function testItCompilesExpressionWithMessage(): void { $compiled = $this->expressionLanguage->compile('throw_not_found_on_null(value, "Not found")', ['value']); $this->assertIsString($compiled); $this->assertStringContainsString('null !== $value', $compiled); $this->assertStringContainsString('NotFoundHttpException("Not found")', $compiled); $this->assertSame( '(null !== $value) ? $value : throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException("Not found")', $compiled, ); } public function testItCompilesExpressionWithComplexMessage(): void { $compiled = $this->expressionLanguage->compile( 'throw_not_found_on_null(value, "Resource not found with the given parameters")', ['value'], ); $this->assertIsString($compiled); $this->assertStringContainsString('NotFoundHttpException("Resource not found with the given parameters")', $compiled); } } ================================================ FILE: src/Component/tests/Symfony/ExpressionLanguage/RequestVariablesTest.php ================================================ requestStack = $this->createMock(RequestStack::class); $this->requestVariables = new RequestVariables($this->requestStack); } public function testItIsInitializable(): void { $this->assertInstanceOf(RequestVariables::class, $this->requestVariables); } public function testItReturnsRequestVars(): void { $request = $this->createMock(Request::class); $this->requestStack->method('getCurrentRequest')->willReturn($request); $result = $this->requestVariables->getVariables(); $this->assertSame(['request' => $request], $result); } } ================================================ FILE: src/Component/tests/Symfony/ExpressionLanguage/SyliusRepositoriesVariablesTest.php ================================================ createMock(ContainerInterface::class); $syliusRepositoriesVariables = new SyliusRepositoriesVariables($syliusRepositories); $this->assertSame(['sylius_repositories' => $syliusRepositories], $syliusRepositoriesVariables->getVariables()); } } ================================================ FILE: src/Component/tests/Symfony/ExpressionLanguage/TokenVariablesTest.php ================================================ tokenStorage = $this->createMock(TokenStorageInterface::class); $this->tokenVariables = new TokenVariables($this->tokenStorage); } public function testItIsInitializable(): void { $this->assertInstanceOf(TokenVariables::class, $this->tokenVariables); } public function testItReturnsTokenAndUserVars(): void { $token = $this->createMock(TokenInterface::class); $user = $this->createMock(UserInterface::class); $this->tokenStorage->method('getToken')->willReturn($token); $token->method('getUser')->willReturn($user); $result = $this->tokenVariables->getVariables(); $this->assertSame(['token' => $token, 'user' => $user], $result); } public function testItReturnsANullTokenIfThereIsNoTokenOnStorage(): void { $this->tokenStorage->method('getToken')->willReturn(null); $result = $this->tokenVariables->getVariables(); $this->assertInstanceOf(NullToken::class, $result['token']); } public function testItCanReturnNullAsUser(): void { $token = $this->createMock(TokenInterface::class); $token->method('getUser')->willReturn(null); $this->tokenStorage->method('getToken')->willReturn($token); $result = $this->tokenVariables->getVariables(); $this->assertSame(['token' => $token, 'user' => null], $result); } public function testItThrowsAnExceptionWhenThereIsNoTokenStorage(): void { $tokenVariables = new TokenVariables(null); $this->expectException(\LogicException::class); $this->expectExceptionMessage('The "symfony/security-bundle" must be installed and configured to use the "token" & "user" attribute. Try running "composer require symfony/security-bundle"'); $tokenVariables->getVariables(); } } ================================================ FILE: src/Component/tests/Symfony/ExpressionLanguage/VariablesCollectionTest.php ================================================ createMock(VariablesInterface::class); $secondVariables = $this->createMock(VariablesInterface::class); $variablesCollection = new VariablesCollection([$firstVariables, $secondVariables]); $this->assertInstanceOf(VariablesCollection::class, $variablesCollection); } public function testItMergesVariables(): void { $firstVariables = $this->createMock(VariablesInterface::class); $secondVariables = $this->createMock(VariablesInterface::class); $firstVariables->method('getVariables')->willReturn(['foo' => 'bar', 'user' => '123']); $secondVariables->method('getVariables')->willReturn(['foo' => 'fighters', 'value' => 'xyz']); $variablesCollection = new VariablesCollection([$firstVariables, $secondVariables]); $result = $variablesCollection->getVariables(); $this->assertSame([ 'foo' => 'fighters', 'user' => '123', 'value' => 'xyz', ], $result); } } ================================================ FILE: src/Component/tests/Symfony/ExpressionLanguage/VarsResolverTest.php ================================================ varsResolver = $container->get('sylius.expression_language.vars_resolver.metadata'); } public function testItIsInitializable(): void { $this->assertInstanceOf(VarsResolverInterface::class, $this->varsResolver); } public function testItResolvesSimpleExpression(): void { $result = $this->varsResolver->resolve(['has_user' => '@=token.getUser() === null']); $this->assertIsArray($result); $this->assertArrayHasKey('has_user', $result); $this->assertTrue($result['has_user']); } public function testItResolvesNestedExpression(): void { $result = $this->varsResolver->resolve(['parameters' => ['has_user' => '@=token.getUser() === null']]); $this->assertIsArray($result); $this->assertArrayHasKey('parameters', $result); $this->assertIsArray($result['parameters']); $this->assertArrayHasKey('has_user', $result['parameters']); $this->assertTrue($result['parameters']['has_user']); } public function testItDoesNotProcessStringValuesWithoutExpressionPrefix(): void { $result = $this->varsResolver->resolve(['name' => 'John']); $this->assertIsArray($result); $this->assertArrayHasKey('name', $result); $this->assertSame('John', $result['name']); } public function testItDoesNotProcessNumericStrings(): void { $result = $this->varsResolver->resolve(['count' => '123']); $this->assertIsArray($result); $this->assertArrayHasKey('count', $result); $this->assertSame('123', $result['count']); } public function testItDoesNotProcessBooleanStrings(): void { $result = $this->varsResolver->resolve(['flag' => 'true']); $this->assertIsArray($result); $this->assertArrayHasKey('flag', $result); $this->assertSame('true', $result['flag']); } public function testItDoesNotProcessEmptyStrings(): void { $result = $this->varsResolver->resolve(['empty' => '']); $this->assertIsArray($result); $this->assertArrayHasKey('empty', $result); $this->assertSame('', $result['empty']); } public function testItHandlesMixedValuesWithAndWithoutExpressions(): void { $result = $this->varsResolver->resolve([ 'name' => 'John', 'has_user' => '@=token.getUser() === null', ]); $this->assertIsArray($result); $this->assertCount(2, $result); $this->assertArrayHasKey('name', $result); $this->assertArrayHasKey('has_user', $result); $this->assertSame('John', $result['name']); $this->assertTrue($result['has_user']); } public function testItHandlesNestedArraysWithMixedValues(): void { $result = $this->varsResolver->resolve([ 'parameters' => [ 'name' => 'John', 'has_user' => '@=token.getUser() === null', ], ]); $this->assertIsArray($result); $this->assertArrayHasKey('parameters', $result); $this->assertIsArray($result['parameters']); $this->assertCount(2, $result['parameters']); $this->assertSame('John', $result['parameters']['name']); $this->assertTrue($result['parameters']['has_user']); } public function testItReturnsEmptyArrayWhenGivenEmptyArray(): void { $result = $this->varsResolver->resolve([]); $this->assertIsArray($result); $this->assertEmpty($result); } public function testItResolvesMultipleExpressions(): void { $result = $this->varsResolver->resolve([ 'has_user' => '@=token.getUser() === null', 'is_authenticated' => '@=token.getUser() !== null', ]); $this->assertIsArray($result); $this->assertCount(2, $result); $this->assertArrayHasKey('has_user', $result); $this->assertArrayHasKey('is_authenticated', $result); $this->assertTrue($result['has_user']); $this->assertFalse($result['is_authenticated']); } public function testItHandlesDeeplyNestedArrays(): void { $result = $this->varsResolver->resolve([ 'level1' => [ 'level2' => [ 'has_user' => '@=token.getUser() === null', 'name' => 'test', ], ], ]); $this->assertIsArray($result); $this->assertArrayHasKey('level1', $result); $this->assertIsArray($result['level1']); $this->assertArrayHasKey('level2', $result['level1']); $this->assertIsArray($result['level1']['level2']); $this->assertTrue($result['level1']['level2']['has_user']); $this->assertSame('test', $result['level1']['level2']['name']); } } ================================================ FILE: src/Component/tests/Symfony/Form/Factory/FormFactoryTest.php ================================================ symfonyFormFactory = $this->createMock(SymfonyFormFactoryInterface::class); $this->argumentParser = $this->createMock(ArgumentParserInterface::class); $this->formFactory = new FormFactory($this->symfonyFormFactory, $this->argumentParser); } public function testItIsInitializable(): void { $this->assertInstanceOf(FormFactory::class, $this->formFactory); } public function testItCreatesAForm(): void { $operation = $this->createMock(Operation::class); $form = $this->createMock(FormInterface::class); $operation->method('getFormType')->willReturn('App\Form\DummyType'); $operation->method('getFormOptions')->willReturn(['foo' => 'fighters']); $this->symfonyFormFactory ->expects($this->once()) ->method('createNamed') ->with('', 'App\Form\DummyType', null, ['foo' => 'fighters', 'csrf_protection' => false]) ->willReturn($form); $result = $this->formFactory->create($operation, new Context()); $this->assertSame($form, $result); } public function testItCreatesAFormFormHtmlRequest(): void { $operation = $this->createMock(Operation::class); $form = $this->createMock(FormInterface::class); $request = $this->createMock(Request::class); $operation->method('getFormType')->willReturn('App\Form\DummyType'); $operation->method('getFormOptions')->willReturn(['foo' => 'fighters']); $request->method('getRequestFormat')->willReturn('html'); $this->symfonyFormFactory ->expects($this->once()) ->method('create') ->with('App\Form\DummyType', null, ['foo' => 'fighters']) ->willReturn($form); $result = $this->formFactory->create($operation, new Context(new RequestOption($request))); $this->assertSame($form, $result); } public function testItUsesExpressionParserForExpressionLanguagePrefixedFormOptions(): void { $operation = $this->createMock(Operation::class); $form = $this->createMock(FormInterface::class); $request = $this->createMock(Request::class); $operation->method('getFormType')->willReturn('App\Form\DummyType'); $operation->method('getFormOptions')->willReturn(['customer' => '@=sylius_context_shopper.getCustomer()']); $request->method('getRequestFormat')->willReturn('html'); $customer = new \StdClass(); $this->argumentParser ->expects($this->exactly(1)) ->method('parseExpression') ->with('sylius_context_shopper.getCustomer()') ->willReturn($customer); $this->symfonyFormFactory ->expects($this->once()) ->method('create') ->with('App\Form\DummyType', null, ['customer' => $customer]) ->willReturn($form); $result = $this->formFactory->create($operation, new Context(new RequestOption($request))); $this->assertSame($form, $result); } public function testItThrowsAnExceptionWhenOperationHasNoFormType(): void { $operation = $this->createMock(Operation::class); $operation->method('getFormType')->willReturn(null); $operation->method('getFormOptions')->willReturn([]); $operation->method('getName')->willReturn('app_dummy_create'); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Operation "app_dummy_create" has no configured form type.'); $this->formFactory->create($operation, new Context()); } } ================================================ FILE: src/Component/tests/Symfony/Form/State/FormProviderTest.php ================================================ decorated = $this->createMock(ProviderInterface::class); $this->formFactory = $this->createMock(FormFactoryInterface::class); $this->formProvider = new FormProvider( $this->decorated, $this->formFactory, ); } public function testItIsInitializable(): void { $this->assertInstanceOf(FormProvider::class, $this->formProvider); } public function testItHandlesFormsForCreateOperation(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $form = $this->createMock(FormInterface::class); $data = ['foo' => 'fighters']; $request->attributes = $attributes; $request ->expects($this->once()) ->method('getRequestFormat') ->willReturn('html'); $operation = new Create(formType: 'App\Type\DummyType'); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->formFactory ->expects($this->once()) ->method('create') ->with($operation, $context, $data) ->willReturn($form); $form ->expects($this->once()) ->method('handleRequest') ->with($request) ->willReturn($form); $attributes ->expects($this->once()) ->method('set') ->with('form', $form); $result = $this->formProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItHandlesFormsForUpdateOperation(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $form = $this->createMock(FormInterface::class); $data = new \stdClass(); $request->attributes = $attributes; $request ->expects($this->once()) ->method('getRequestFormat') ->willReturn('html'); $operation = new Update(formType: 'App\Type\DummyType'); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->formFactory ->expects($this->once()) ->method('create') ->with($operation, $context, $data) ->willReturn($form); $form ->expects($this->once()) ->method('handleRequest') ->with($request) ->willReturn($form); $attributes ->expects($this->once()) ->method('set') ->with('form', $form); $result = $this->formProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItDoesNothingWhenDataIsAResponse(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $response = $this->createMock(Response::class); $request->attributes = $attributes; $request ->expects($this->once()) ->method('getRequestFormat') ->willReturn('html'); $operation = new Create(formType: 'App\Type\DummyType'); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($response); $this->formFactory ->expects($this->never()) ->method('create'); $attributes ->expects($this->never()) ->method('set'); $result = $this->formProvider->provide($operation, $context); $this->assertSame($response, $result); } public function testItDoesNothingWhenOperationHasNoFormType(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $data = ['foo' => 'bar']; $request->attributes = $attributes; $request ->expects($this->once()) ->method('getRequestFormat') ->willReturn('html'); $operation = new Create(formType: null); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->formFactory ->expects($this->never()) ->method('create'); $attributes ->expects($this->never()) ->method('set'); $result = $this->formProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItDoesNothingWhenOperationIsNotACreateOrUpdate(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $data = ['foo' => 'bar']; $request->attributes = $attributes; $request ->expects($this->once()) ->method('getRequestFormat') ->willReturn('html'); $operation = new Show(formType: 'App\Type\DummyType'); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->formFactory ->expects($this->never()) ->method('create'); $attributes ->expects($this->never()) ->method('set'); $result = $this->formProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItDoesNothingWhenOperationIsABulkUpdate(): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $data = ['foo' => 'bar']; $request->attributes = $attributes; $request ->expects($this->once()) ->method('getRequestFormat') ->willReturn('html'); $operation = new BulkUpdate(formType: 'App\Type\DummyType'); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->formFactory ->expects($this->never()) ->method('create'); $attributes ->expects($this->never()) ->method('set'); $result = $this->formProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItReturnsDataWhenRequestIsNull(): void { $data = ['foo' => 'bar']; $operation = new Create(formType: 'App\Type\DummyType'); $context = new Context(); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->formFactory ->expects($this->never()) ->method('create'); $result = $this->formProvider->provide($operation, $context); $this->assertSame($data, $result); } /** * @dataProvider nonHtmlRequestFormatProvider */ public function testItDoesNothingWhenRequestFormatIsNotHtml(string $requestFormat): void { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $data = ['foo' => 'bar']; $request->attributes = $attributes; $request ->expects($this->once()) ->method('getRequestFormat') ->willReturn($requestFormat); $operation = new Create(formType: 'App\Type\DummyType'); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->formFactory ->expects($this->never()) ->method('create'); $attributes ->expects($this->never()) ->method('set'); $result = $this->formProvider->provide($operation, $context); $this->assertSame($data, $result); } /** * @return iterable> */ public static function nonHtmlRequestFormatProvider(): iterable { yield 'json' => ['json']; yield 'xml' => ['xml']; } } ================================================ FILE: src/Component/tests/Symfony/Request/RepositoryArgumentResolverTest.php ================================================ repositoryArgumentResolver = new RepositoryArgumentResolver(); } public function testItIsInitializable(): void { $this->assertInstanceOf(RepositoryArgumentResolver::class, $this->repositoryArgumentResolver); } public function testItGetsArgumentsToSentToTheRepository(): void { $request = $this->createRequest(['id' => 'my_id'], [], []); $callable = [RepositoryWithCallables::class, 'find']; $reflector = CallableReflection::from($callable); $result = $this->repositoryArgumentResolver->getArguments($request, $reflector); $this->assertSame(['id' => 'my_id'], $result); } public function testItUsesQueryParamsWhenRouteParamsAreNotMatching(): void { $request = $this->createRequest( ['_sylius' => ['resource' => 'app.dummy']], ['id' => 'my_id'], [], ); $callable = [RepositoryWithCallables::class, 'find']; $reflector = CallableReflection::from($callable); $result = $this->repositoryArgumentResolver->getArguments($request, $reflector); $this->assertSame(['id' => 'my_id'], $result); } public function testItUsesRequestParamsWhenRouteParamsAreNotMatching(): void { $request = $this->createRequest( ['_sylius' => ['resource' => 'app.dummy']], [], ['id' => 'my_id'], ); $callable = [RepositoryWithCallables::class, 'find']; $reflector = CallableReflection::from($callable); $result = $this->repositoryArgumentResolver->getArguments($request, $reflector); $this->assertSame(['id' => 'my_id'], $result); } public function testItEncapsulatesArgumentsWhenTheMethodHasOnlyOneRequiredArrayArgument(): void { $request = $this->createRequest( ['enabled' => 'true', 'author' => 'author@example.com'], [], [], ); $callable = [RepositoryWithCallables::class, 'findOneBy']; $reflector = CallableReflection::from($callable); $result = $this->repositoryArgumentResolver->getArguments($request, $reflector); $this->assertSame([['enabled' => 'true', 'author' => 'author@example.com']], $result); } public function testItReturnArrayValuesWhenMethodIsMagic(): void { $request = $this->createRequest( ['_sylius' => ['resource' => 'app.dummy']], [], ['ids' => ['first_id', 'second_id']], ); $callable = [new RepositoryWithCallables(), '__call']; $reflector = CallableReflection::from($callable); $result = $this->repositoryArgumentResolver->getArguments($request, $reflector); $this->assertSame([['first_id', 'second_id']], $result); } private function createRequest(array $routeParams, array $queryParams, array $requestParams): Request { $request = new Request(); $request->attributes = new ParameterBag(); $request->query = new InputBag($queryParams); $request->request = new InputBag($requestParams); $attributesMock = $this->createMock(ParameterBag::class); $attributesMock->method('all')->with('_route_params')->willReturn($routeParams); $request->attributes = $attributesMock; return $request; } } ================================================ FILE: src/Component/tests/Symfony/Request/State/ApiResponderTest.php ================================================ apiResponder = new ApiResponder(new ApiHeadersInitiator()); } public function testItIsInitializable(): void { $this->assertInstanceOf(ApiResponder::class, $this->apiResponder); } public function testItReturnsAResponseWithHttpCreatedForResourceCreate(): void { $request = $this->createRequest(); $context = new Context(new RequestOption($request)); $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); $operation = (new Create())->withResource($resource); $response = $this->apiResponder->respond('serialized_data', $operation, $context); $this->assertInstanceOf(Response::class, $response); $this->assertSame(Response::HTTP_CREATED, $response->getStatusCode()); } public function testItReturnsAResponseWithHttpNoContentForResourceUpdate(): void { $request = $this->createRequest(); $context = new Context(new RequestOption($request)); $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); $operation = (new Update())->withResource($resource); $response = $this->apiResponder->respond('serialized_data', $operation, $context); $this->assertInstanceOf(Response::class, $response); $this->assertSame(Response::HTTP_NO_CONTENT, $response->getStatusCode()); } public function testItReturnsAResponseWithHttpNoContentForResourceDelete(): void { $request = $this->createRequest(); $context = new Context(new RequestOption($request)); $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); $operation = (new Delete())->withResource($resource); $response = $this->apiResponder->respond('serialized_data', $operation, $context); $this->assertInstanceOf(Response::class, $response); $this->assertSame(Response::HTTP_NO_CONTENT, $response->getStatusCode()); } public function testItReturnsAResponseWithHttpUnprocessableEntityForInvalidResource(): void { $request = $this->createRequest(false); $context = new Context(new RequestOption($request)); $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); $operation = (new Create())->withResource($resource); $response = $this->apiResponder->respond('serialized_data', $operation, $context); $this->assertInstanceOf(Response::class, $response); $this->assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response->getStatusCode()); } private function createRequest(bool $isValid = true): Request { $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $request->attributes = $attributes; $request->method('getRequestFormat')->willReturn('json'); $request->method('getMimeType')->with('json')->willReturn('application/json'); $attributes->method('getBoolean')->with('is_valid', true)->willReturn($isValid); $attributes->method('get')->with('form')->willReturn(null); return $request; } } ================================================ FILE: src/Component/tests/Symfony/Request/State/ProviderTest.php ================================================ locator = $this->createMock(ContainerInterface::class); $this->argumentParser = $this->createMock(ArgumentParserInterface::class); $this->provider = new Provider($this->locator, new RepositoryArgumentResolver(), $this->argumentParser); } public function testItIsInitializable(): void { $this->assertInstanceOf(Provider::class, $this->provider); } public function testItCallsRepositoryAsCallable(): void { $operation = $this->createMock(Operation::class); $request = $this->createRequest(['id' => 'my_id'], [], []); $operation->method('getRepository')->willReturn([RepositoryWithCallables::class, 'find']); $operation->method('getRepositoryArguments')->willReturn(null); $response = $this->provider->provide($operation, new Context(new RequestOption($request))); $this->assertInstanceOf(\stdClass::class, $response); $this->assertSame('my_id', $response->id); } public function testItCallsRepositoryAsString(): void { $operation = $this->createMock(Operation::class); $request = $this->createRequest(['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']], [], []); $repository = $this->createMock(RepositoryInterface::class); $stdClass = new \stdClass(); $operation->method('getRepository')->willReturn('App\Repository'); $operation->method('getRepositoryMethod')->willReturn(null); $operation->method('getRepositoryArguments')->willReturn(null); $this->locator->method('has')->with('App\Repository')->willReturn(true); $this->locator->method('get')->with('App\Repository')->willReturn($repository); $repository->method('findOneBy')->with(['id' => 'my_id'])->willReturn($stdClass); $response = $this->provider->provide($operation, new Context(new RequestOption($request))); $this->assertSame($stdClass, $response); } public function testItCallsCreatePaginatorByDefaultOnCollectionOperations(): void { $operation = new Index(repository: 'App\Repository'); $request = $this->createRequest(['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']], [], []); $repository = $this->createMock(RepositoryInterface::class); $pagerfanta = $this->createMock(Pagerfanta::class); $this->locator->method('has')->with('App\Repository')->willReturn(true); $this->locator->method('get')->with('App\Repository')->willReturn($repository); $repository->expects($this->once())->method('createPaginator')->willReturn($pagerfanta); $pagerfanta->expects($this->once())->method('setCurrentPage')->with(1)->willReturnSelf(); $response = $this->provider->provide($operation, new Context(new RequestOption($request))); $this->assertSame($pagerfanta, $response); } public function testItSetsCurrentPageFromRequestWhenDataIsAPaginator(): void { $operation = new Index(repository: 'App\Repository'); $request = $this->createRequest(['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']], ['page' => 42], []); $repository = $this->createMock(RepositoryInterface::class); $pagerfanta = $this->createMock(Pagerfanta::class); $this->locator->method('has')->with('App\Repository')->willReturn(true); $this->locator->method('get')->with('App\Repository')->willReturn($repository); $repository->expects($this->once())->method('createPaginator')->willReturn($pagerfanta); $pagerfanta->expects($this->once())->method('setCurrentPage')->with(42)->willReturnSelf(); $pagerfanta->method('getCurrentPage')->willReturn(42); $response = $this->provider->provide($operation, new Context(new RequestOption($request))); $this->assertSame($pagerfanta, $response); $this->assertSame(42, $pagerfanta->getCurrentPage()); } public function testItCallsRepositoryAsStringWithSpecificRepositoryMethod(): void { $operation = $this->createMock(Operation::class); $request = $this->createRequest(['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']], [], []); $repository = $this->createMock(RepositoryInterface::class); $stdClass = new \stdClass(); $operation->method('getRepository')->willReturn('App\Repository'); $operation->method('getRepositoryMethod')->willReturn('find'); $operation->method('getRepositoryArguments')->willReturn(null); $this->locator->method('has')->with('App\Repository')->willReturn(true); $this->locator->method('get')->with('App\Repository')->willReturn($repository); $repository->method('find')->with('my_id')->willReturn($stdClass); $response = $this->provider->provide($operation, new Context(new RequestOption($request))); $this->assertSame($stdClass, $response); } public function testItCallsRepositoryAsStringWithSpecificRepositoryMethodAndArguments(): void { $operation = $this->createMock(Operation::class); $request = $this->createMock(Request::class); $repository = $this->createMock(RepositoryInterface::class); $stdClass = new \stdClass(); $operation->method('getRepository')->willReturn('App\Repository'); $operation->method('getRepositoryMethod')->willReturn('find'); $operation->method('getRepositoryArguments')->willReturn(['id' => "request.attributes.get('id')"]); $this->argumentParser ->expects($this->once()) ->method('parseExpression') ->with("request.attributes.get('id')") ->willReturn('my_id'); $this->locator->method('has')->with('App\Repository')->willReturn(true); $this->locator->method('get')->with('App\Repository')->willReturn($repository); $repository->method('find')->with('my_id')->willReturn($stdClass); $response = $this->provider->provide($operation, new Context(new RequestOption($request))); $this->assertSame($stdClass, $response); } public function testItCallsRepositoryAsStringWithSpecificRepositoryMethodAndExpressionLanguagePrefixedArguments(): void { $operation = $this->createMock(Operation::class); $request = $this->createMock(Request::class); $repository = $this->createMock(RepositoryInterface::class); $stdClass = new \stdClass(); $operation->method('getRepository')->willReturn('App\Repository'); $operation->method('getRepositoryMethod')->willReturn('find'); $operation->method('getRepositoryArguments')->willReturn(['id' => "@=request.attributes.get('id')"]); $this->argumentParser ->expects($this->once()) ->method('parseExpression') ->with("request.attributes.get('id')") ->willReturn('my_id'); $this->locator->method('has')->with('App\Repository')->willReturn(true); $this->locator->method('get')->with('App\Repository')->willReturn($repository); $repository->method('find')->with('my_id')->willReturn($stdClass); $response = $this->provider->provide($operation, new Context(new RequestOption($request))); $this->assertSame($stdClass, $response); } public function testItParsesNestedArrayArguments(): void { $operation = $this->createMock(Operation::class); $request = $this->createMock(Request::class); $repository = $this->createMock(RepositoryInterface::class); $stdClass = new \stdClass(); $operation->method('getRepository')->willReturn('App\Repository'); $operation->method('getRepositoryMethod')->willReturn('findOneBy'); $operation->method('getRepositoryArguments')->willReturn([['tokenValue' => "@=request.attributes.get('tokenValue')"]]); $this->argumentParser ->expects($this->once()) ->method('parseExpression') ->with("request.attributes.get('tokenValue')") ->willReturn('my_token'); $this->locator->method('has')->with('App\Repository')->willReturn(true); $this->locator->method('get')->with('App\Repository')->willReturn($repository); $repository->method('findOneBy')->with(['tokenValue' => 'my_token'])->willReturn($stdClass); $response = $this->provider->provide($operation, new Context(new RequestOption($request))); $this->assertSame($stdClass, $response); } public function testItThrowsAnExceptionWhenRepositoryMethodDoesNotExist(): void { $operation = $this->createMock(Operation::class); $request = $this->createMock(Request::class); $operation->method('getRepository')->willReturn('App\Repository'); $operation->method('getRepositoryMethod')->willReturn('notFoundMethod'); $operation->method('getRepositoryArguments')->willReturn(['id' => "request.attributes.get('id')"]); $this->locator->method('has')->with('App\Repository')->willReturn(true); $this->locator->method('get')->with('App\Repository')->willReturn(new \stdClass()); $errorMessage = sprintf( 'Method "notFoundMethod" not found on repository "%s". You can either add it or configure another one in the repositoryMethod option for your operation.', \stdClass::class, ); $this->expectException(RuntimeException::class); $this->expectExceptionMessage($errorMessage); $this->provider->provide($operation, new Context(new RequestOption($request))); } public function testItThrowsAnExceptionWhenRepositoryMethodDoesNotExistAndSuggestToUseCreatePaginatorIfItIsAppropriated(): void { $operation = $this->createMock(Operation::class); $request = $this->createMock(Request::class); $operation->method('getRepository')->willReturn('App\Repository'); $operation->method('getRepositoryMethod')->willReturn('createPaginator'); $operation->method('getRepositoryArguments')->willReturn(['id' => "request.attributes.get('id')"]); $this->locator->method('has')->with('App\Repository')->willReturn(true); $this->locator->method('get')->with('App\Repository')->willReturn(new \stdClass()); $errorMessage = sprintf( 'Method "createPaginator" not found on repository "%s". You can use the "%s" trait on this repository class.', \stdClass::class, CreatePaginatorTrait::class, ); $this->expectException(RuntimeException::class); $this->expectExceptionMessage($errorMessage); $this->provider->provide($operation, new Context(new RequestOption($request))); } public function testItThrowsAnExceptionWhenRepositoryArgumentsAreNotScalarOnes(): void { $operation = $this->createMock(Operation::class); $request = $this->createMock(Request::class); $repository = $this->createMock(RepositoryInterface::class); $operation->method('getRepository')->willReturn('App\Repository'); $operation->method('getRepositoryMethod')->willReturn('findOneBy'); $operation->method('getRepositoryArguments')->willReturn([['foo' => 'resource.code', 'bar' => new \stdClass()]]); $this->locator->method('has')->with('App\Repository')->willReturn(true); $this->locator->method('get')->with('App\Repository')->willReturn($repository); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Parameter "bar" should be a scalar or an array.'); $this->provider->provide($operation, new Context(new RequestOption($request))); } private function createRequest(array $routeParams, array $queryParams, array $requestParams): Request { $request = new Request(); $request->attributes = new ParameterBag(['_route_params' => $routeParams]); $request->query = new InputBag($queryParams); $request->request = new InputBag($requestParams); return $request; } } ================================================ FILE: src/Component/tests/Symfony/Request/State/ResponderTest.php ================================================ locator = $this->createMock(ContainerInterface::class); $this->responder = new Responder($this->locator); } public function testItIsInitializable(): void { $this->assertInstanceOf(Responder::class, $this->responder); } public function testItUsesHtmlResponderOnHtmlFormat(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $htmlResponder = $this->createMock(ResponderInterface::class); $operation = $this->createMock(HttpOperation::class); $response = $this->createMock(Response::class); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('html'); $this->locator->method('has')->with('sylius.state_responder.html')->willReturn(true); $this->locator->method('get')->with('sylius.state_responder.html')->willReturn($htmlResponder); $htmlResponder ->expects($this->once()) ->method('respond') ->with($data, $operation, $context) ->willReturn($response); $this->responder->respond($data, $operation, $context); } public function testItUsesApiResponderOnJsonFormat(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $apiResponder = $this->createMock(ResponderInterface::class); $operation = $this->createMock(HttpOperation::class); $response = $this->createMock(Response::class); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('json'); $this->locator->method('has')->with('sylius.state_responder.api')->willReturn(true); $this->locator->method('get')->with('sylius.state_responder.api')->willReturn($apiResponder); $apiResponder ->expects($this->once()) ->method('respond') ->with($data, $operation, $context) ->willReturn($response); $this->responder->respond($data, $operation, $context); } public function testItThrowAnExceptionWhenHtmlResponderWasNotFound(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('html'); $this->locator->method('has')->with('sylius.state_responder.html')->willReturn(false); $this->expectException(\LogicException::class); $this->expectExceptionMessage('Responder "sylius.state_responder.html" was not found but it should.'); $this->responder->respond($data, $operation, $context); } public function testItThrowAnExceptionWhenJsonResponderWasNotFound(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $operation = $this->createMock(HttpOperation::class); $context = new Context(new RequestOption($request)); $request->method('getRequestFormat')->willReturn('json'); $this->locator->method('has')->with('sylius.state_responder.api')->willReturn(false); $this->expectException(\LogicException::class); $this->expectExceptionMessage('Responder "sylius.state_responder.api" was not found but it should.'); $this->responder->respond($data, $operation, $context); } } ================================================ FILE: src/Component/tests/Symfony/Request/State/TwigResponderTest.php ================================================ twig = $this->createMock(Environment::class); $this->redirectHandler = $this->createMock(RedirectHandlerInterface::class); $this->contextFactory = $this->createMock(ContextFactoryInterface::class); $this->twigResponder = new TwigResponder($this->redirectHandler, $this->contextFactory, $this->twig); } public function testItIsInitializable(): void { $this->assertInstanceOf(TwigResponder::class, $this->twigResponder); } public function testItReturnsAResponseForResourceShow(): void { $data = new \stdClass(); $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $context = new Context(new RequestOption($request)); $request->attributes = $attributes; $request->method('isMethodSafe')->willReturn(true); $attributes->expects($this->once())->method('getBoolean')->with('is_valid', true)->willReturn(false); $attributes->method('get')->with('form')->willReturn(null); $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); $operation = (new Show(template: 'book/show.html.twig'))->withResource($resource); $this->contextFactory ->expects($this->once()) ->method('create') ->with($data, $operation, $context) ->willReturn(['book' => $data]); $this->twig ->expects($this->once()) ->method('render') ->with('book/show.html.twig', ['book' => $data]) ->willReturn('result'); $response = $this->twigResponder->respond($data, $operation, $context); $this->assertSame(200, $response->getStatusCode()); } public function testItReturnsAResponseForResourceIndex(): void { $data = new \ArrayObject(); $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $context = new Context(new RequestOption($request)); $request->attributes = $attributes; $request->method('isMethodSafe')->willReturn(true); $attributes->expects($this->once())->method('getBoolean')->with('is_valid', true)->willReturn(true); $attributes->method('get')->with('form')->willReturn(null); $resource = new ResourceMetadata(alias: 'app.book', pluralName: 'books'); $operation = (new Index(template: 'book/index.html.twig'))->withResource($resource); $this->contextFactory ->expects($this->once()) ->method('create') ->with($data, $operation, $context) ->willReturn(['books' => $data]); $this->twig ->expects($this->once()) ->method('render') ->with('book/index.html.twig', ['books' => $data]) ->willReturn('result'); $this->twigResponder->respond($data, $operation, $context); } public function testItRedirectToRouteAfterCreation(): void { $data = new \ArrayObject(); $data['id'] = 'xyz'; $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $redirectResponse = $this->createMock(RedirectResponse::class); $request->attributes = $attributes; $request->method('isMethodSafe')->willReturn(false); $attributes->expects($this->once())->method('getBoolean')->with('is_valid', true)->willReturn(true); $operation = new Create(); $this->redirectHandler ->expects($this->once()) ->method('redirectToResource') ->with($data, $operation, $request) ->willReturn($redirectResponse); $result = $this->twigResponder->respond($data, $operation, new Context(new RequestOption($request))); $this->assertSame($redirectResponse, $result); } public function testItResponseIsUnprocessableWhenValidationHasFailed(): void { $data = new \ArrayObject(); $data['id'] = 'xyz'; $request = $this->createMock(Request::class); $attributes = $this->createMock(ParameterBag::class); $context = new Context(new RequestOption($request)); $request->attributes = $attributes; $request->method('isMethodSafe')->willReturn(false); $attributes->expects($this->once())->method('getBoolean')->with('is_valid', true)->willReturn(false); $operation = new Create(); $this->contextFactory ->expects($this->once()) ->method('create') ->with($data, $operation, $context) ->willReturn(['books' => $data]); $this->twig ->expects($this->once()) ->method('render') ->with('', ['books' => $data]) ->willReturn('twig_content'); $response = $this->twigResponder->respond($data, $operation, new Context(new RequestOption($request))); $this->assertSame(422, $response->getStatusCode()); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/AttributesOperationRouteFactoryTest.php ================================================ resourceRegistry = new Registry(); $this->routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); $this->attributesOperationRouteFactory = new AttributesOperationRouteFactory( $this->resourceRegistry, new OperationRouteFactory($this->routePathFactory, new Operation\DashPathSegmentNameGenerator(new Inflector()), false), new AttributesResourceMetadataCollectionFactory( $this->resourceRegistry, new OperationRouteNameFactory(), ), ); } public function testItCreatesRoutesWithOperations(): void { $routeCollection = new RouteCollection(); $this->resourceRegistry->add(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', ])); $this->routePathFactory ->expects($this->exactly(4)) ->method('createRoutePath') ->willReturnCallback(function ($operation, $path) { if ($operation instanceof Index) { return '/dummies'; } if ($operation instanceof Create) { return '/dummies/new'; } if ($operation instanceof Update) { return '/dummies/{id}/edit'; } if ($operation instanceof Show) { return '/dummies/{id}'; } return $path; }); $this->attributesOperationRouteFactory->createRouteForClass($routeCollection, DummyResourceWithOperations::class); $this->assertCount(4, $routeCollection); $this->assertNotNull($routeCollection->get('app_dummy_index'), 'Route "app_dummy_index" not found but it should.'); $this->assertNotNull($routeCollection->get('app_dummy_create'), 'Route "app_dummy_create" not found but it should.'); $this->assertNotNull($routeCollection->get('app_dummy_update'), 'Route "app_dummy_update" not found but it should.'); $this->assertNotNull($routeCollection->get('app_dummy_show'), 'Route "app_dummy_show" not found but it should.'); } public function testItSkipsNonHttpOperations(): void { $routeCollection = new RouteCollection(); $this->resourceRegistry->add(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', ])); $nonHttpOperation = new class() extends Operation { public function getShortName(): ?string { return 'custom'; } }; $httpOperation = (new Index(name: 'app_dummy_index'))->withRouteName('app_dummy_index'); $resource = new ResourceMetadata( alias: 'app.dummy', name: 'dummy', pluralName: 'dummies', operations: [ 'app_dummy_custom' => $nonHttpOperation, 'app_dummy_index' => $httpOperation, ], ); $resourceCollection = new ResourceMetadataCollection(); $resourceCollection[] = $resource; $resourceMetadataFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $resourceMetadataFactory ->method('create') ->with(\stdClass::class) ->willReturn($resourceCollection); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($httpOperation, 'dummies') ->willReturn('/dummies'); $factory = new AttributesOperationRouteFactory( $this->resourceRegistry, new OperationRouteFactory($this->routePathFactory, new Operation\DashPathSegmentNameGenerator(new Inflector()), false), $resourceMetadataFactory, ); $factory->createRouteForClass($routeCollection, \stdClass::class); $this->assertCount(1, $routeCollection); $this->assertNotNull($routeCollection->get('app_dummy_index'), 'Route "app_dummy_index" not found but it should.'); $this->assertNull($routeCollection->get('app_dummy_custom'), 'Non-HTTP operation should not create a route.'); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/OperationRouteFactoryTest.php ================================================ routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); $this->operationRouteFactory = new OperationRouteFactory($this->routePathFactory, new DashPathSegmentNameGenerator(new Inflector()), false); $this->bcOperationRouteFactory = new OperationRouteFactory($this->routePathFactory, new DashPathSegmentNameGenerator(new Inflector()), true); } public function testItCreatesRouteWithDefaultPathWithBcLayer(): void { $this->markAsSkippedIfBcLayerCannotBeEnabled(); $metadata = $this->createMock(MetadataInterface::class); $metadata->method('getPluralName')->willReturn('books'); $resource = new ResourceMetadata(alias: 'app.book'); $operation = new Index(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, 'books') ->willReturn('/books'); $route = $this->bcOperationRouteFactory->create($metadata, $resource, $operation); $this->assertInstanceOf(Route::class, $route); $this->assertSame('/books', $route->getPath()); $this->assertSame('sylius.main_controller', $route->getDefault('_controller')); $this->assertSame(['resource' => 'app.book'], $route->getDefault('_sylius')); } public function testItCreatesRouteWithDefaultPath(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book', pluralName: 'books'); $operation = new Index(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, 'books') ->willReturn('/books'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertInstanceOf(Route::class, $route); $this->assertSame('/books', $route->getPath()); $this->assertSame('sylius.main_controller', $route->getDefault('_controller')); $this->assertSame(['resource' => 'app.book'], $route->getDefault('_sylius')); } public function testItCreatesRouteWithCustomPath(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book'); $operation = new Index(path: '/custom/books/list'); $this->routePathFactory ->expects($this->never()) ->method('createRoutePath'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame('/custom/books/list', $route->getPath()); } public function testItCreatesRouteWithRoutePrefixWithBcLayer(): void { $this->markAsSkippedIfBcLayerCannotBeEnabled(); $metadata = $this->createMock(MetadataInterface::class); $metadata->method('getPluralName')->willReturn('books'); $resource = new ResourceMetadata(alias: 'app.book'); $operation = (new Index())->withRoutePrefix('/admin'); $this->routePathFactory ->method('createRoutePath') ->with($operation, 'books') ->willReturn('/books'); $route = $this->bcOperationRouteFactory->create($metadata, $resource, $operation); $this->assertSame('/admin/books', $route->getPath()); } public function testItCreatesRouteWithRoutePrefix(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book', pluralName: 'books'); $operation = (new Index())->withRoutePrefix('/admin'); $this->routePathFactory ->method('createRoutePath') ->with($operation, 'books') ->willReturn('/books'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame('/admin/books', $route->getPath()); } public function testItCreatesRouteWithCustomPathAndRoutePrefix(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book'); $operation = new Index(path: '/custom/books/list', routePrefix: '/admin'); $this->routePathFactory ->expects($this->never()) ->method('createRoutePath'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame('/admin/custom/books/list', $route->getPath()); } public function testItCreatesRouteWithCustomPathAndRoutePrefixAndTooManySlashes(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book'); $operation = new Index(path: '/custom/books/list', routePrefix: '/admin/'); $this->routePathFactory ->expects($this->never()) ->method('createRoutePath'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame('/admin/custom/books/list', $route->getPath()); } public function testItCreatesRouteWithSection(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book', section: 'admin'); $operation = new Index(); $this->routePathFactory ->method('createRoutePath') ->willReturn('/books'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $syliusOptions = $route->getDefault('_sylius'); $this->assertSame('app.book', $syliusOptions['resource']); $this->assertSame('admin', $syliusOptions['section']); } public function testItCreatesRouteWithVars(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book'); $operation = (new Index())->withVars(['grid' => 'app_book']); $this->routePathFactory ->method('createRoutePath') ->willReturn('/books'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $syliusOptions = $route->getDefault('_sylius'); $this->assertSame('app.book', $syliusOptions['resource']); $this->assertSame(['grid' => 'app_book'], $syliusOptions['vars']); } public function testItCreatesRouteWithoutVarsWhenNull(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book'); $operation = new Index(); $this->routePathFactory ->method('createRoutePath') ->willReturn('/books'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $syliusOptions = $route->getDefault('_sylius'); $this->assertSame('app.book', $syliusOptions['resource']); $this->assertArrayNotHasKey('vars', $syliusOptions); } public function testItCreatesRouteWithRequirements(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book'); $operation = (new Show())->withRouteRequirements(['id' => '\d+']); $this->routePathFactory ->method('createRoutePath') ->willReturn('/books/{id}'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame(['id' => '\d+'], $route->getRequirements()); } public function testItCreatesRouteWithEmptyRequirementsWhenNull(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book'); $operation = new Index(); $this->routePathFactory ->method('createRoutePath') ->willReturn('/books'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame([], $route->getRequirements()); } public function testItCreatesRouteWithMethods(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book'); $operation = (new Index())->withMethods(['GET', 'POST']); $this->routePathFactory ->method('createRoutePath') ->willReturn('/books'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame(['GET', 'POST'], $route->getMethods()); } public function testItCreatesRouteWithDefaultMethodsForIndex(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata->method('getPluralName')->willReturn('books'); $resource = new ResourceMetadata(alias: 'app.book'); $operation = new Index(); // Index has default method 'GET' $this->routePathFactory ->method('createRoutePath') ->willReturn('/books'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame(['GET'], $route->getMethods()); } public function testItCreatesRouteWithCondition(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata->method('getPluralName')->willReturn('books'); $resource = new ResourceMetadata(alias: 'app.book'); $operation = (new Index())->withRouteCondition('context.getMethod() == "GET"'); $this->routePathFactory ->method('createRoutePath') ->willReturn('/books'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame('context.getMethod() == "GET"', $route->getCondition()); } public function testItCreatesRouteWithEmptyConditionWhenNotSet(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book'); $operation = new Index(); $this->routePathFactory ->method('createRoutePath') ->willReturn('/books'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame('', $route->getCondition()); } public function testItUrlizesPluralName(): void { $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book_category', pluralName: 'Book Categories'); $operation = new Index(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, 'book-categories') ->willReturn('/book-categories'); $route = $this->operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame('/book-categories', $route->getPath()); } public function testItUrlizesPluralNameWithBcLayerEnabled(): void { $this->markAsSkippedIfBcLayerCannotBeEnabled(); $metadata = $this->createMock(MetadataInterface::class); $metadata->method('getPluralName')->willReturn('Book Categories'); $resource = new ResourceMetadata(alias: 'app.book_category'); $operation = new Index(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, 'book-categories') ->willReturn('/book-categories'); $route = $this->bcOperationRouteFactory->create($metadata, $resource, $operation); $this->assertSame('/book-categories', $route->getPath()); } public function testItUrlizesPluralNameUsingUnderscoreAsSeparator(): void { $operationRouteFactory = new OperationRouteFactory($this->routePathFactory, new UnderscorePathSegmentNameGenerator(new Inflector()), false); $metadata = $this->createMock(MetadataInterface::class); $resource = new ResourceMetadata(alias: 'app.book_category', pluralName: 'BookCategories'); $operation = new Index(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, 'book_categories') ->willReturn('/book_categories'); $route = $operationRouteFactory->create($metadata, $resource, $operation); $this->assertSame('/book_categories', $route->getPath()); } private function markAsSkippedIfBcLayerCannotBeEnabled(): void { if (!class_exists(Transliterator::class)) { $this->markTestSkipped('This test requires The Behat Transliterator.'); } } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/Resource/ResourceRouteCollectionFactoryTest.php ================================================ resourceRegistry = new Registry(); $this->routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); $this->factory = new ResourceRouteCollectionFactory( new OperationRouteFactory($this->routePathFactory, new Operation\DashPathSegmentNameGenerator(new Inflector()), false), new AttributesResourceMetadataCollectionFactory( $this->resourceRegistry, new OperationRouteNameFactory(), ), $this->resourceRegistry, ); } public function testItCreatesRoutesWithOperations(): void { $this->resourceRegistry->add(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', ])); $routeCollection = $this->factory->createRouteCollectionForClass(DummyResourceWithOperations::class); $this->assertCount(4, $routeCollection); $this->assertNotNull($routeCollection->get('app_dummy_index'), 'Route "app_dummy_index" not found but it should.'); $this->assertNotNull($routeCollection->get('app_dummy_create'), 'Route "app_dummy_create" not found but it should.'); $this->assertNotNull($routeCollection->get('app_dummy_update'), 'Route "app_dummy_update" not found but it should.'); $this->assertNotNull($routeCollection->get('app_dummy_show'), 'Route "app_dummy_show" not found but it should.'); } public function testItCreatesRoutesWithPriorities(): void { $this->resourceRegistry->add(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', ])); $routeCollection = $this->factory->createRouteCollectionForClass(DummyResourceWithRoutePriorities::class); $this->assertCount(4, $routeCollection); $routeNames = array_keys(iterator_to_array($routeCollection->getIterator())); $this->assertSame([ 'app_dummy_create', 'app_dummy_update', 'app_dummy_index', 'app_dummy_show', ], $routeNames); } public function testItSkipsNonHttpOperations(): void { $nonHttpOperation = new class() extends Operation { public function getShortName(): ?string { return 'custom'; } }; $httpOperation = (new Index(name: 'app_dummy_index'))->withRouteName('app_dummy_index'); $resource = new ResourceMetadata( alias: 'app.dummy', name: 'dummy', pluralName: 'dummies', operations: [ 'app_dummy_custom' => $nonHttpOperation, 'app_dummy_index' => $httpOperation, ], ); $resourceCollection = new ResourceMetadataCollection(); $resourceCollection[] = $resource; $resourceMetadataFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $resourceMetadataFactory ->method('create') ->with(\stdClass::class) ->willReturn($resourceCollection); $this->resourceRegistry->add(Metadata::fromAliasAndConfiguration('app.dummy', [ 'driver' => 'dummy_driver', ])); $factory = new ResourceRouteCollectionFactory( new OperationRouteFactory($this->routePathFactory, new Operation\DashPathSegmentNameGenerator(new Inflector()), false), $resourceMetadataFactory, $this->resourceRegistry, ); $routeCollection = $factory->createRouteCollectionForClass(\stdClass::class); $this->assertCount(1, $routeCollection); $this->assertNotNull($routeCollection->get('app_dummy_index'), 'Route "app_dummy_index" not found but it should.'); $this->assertNull($routeCollection->get('app_dummy_custom'), 'Non-HTTP operation should not create a route.'); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/RouteName/OperationRouteNameFactoryTest.php ================================================ operationRouteNameFactory = new OperationRouteNameFactory(); } public function testItCreatesRouteNameForOperationWithResource(): void { $resource = new ResourceMetadata( alias: 'app.dummy', name: 'dummy', applicationName: 'app', ); $operation = (new Index())->withResource($resource); $result = $this->operationRouteNameFactory->createRouteName($operation); $this->assertSame('app_dummy_index', $result); } public function testItCreatesRouteNameForOperationWithSection(): void { $resource = new ResourceMetadata( alias: 'app.dummy', section: 'admin', name: 'dummy', applicationName: 'app', ); $operation = (new Index())->withResource($resource); $result = $this->operationRouteNameFactory->createRouteName($operation); $this->assertSame('app_admin_dummy_index', $result); } public function testItCreatesRouteNameWithCustomShortName(): void { $resource = new ResourceMetadata( alias: 'app.dummy', name: 'dummy', applicationName: 'app', ); $operation = (new Show())->withResource($resource); $result = $this->operationRouteNameFactory->createRouteName($operation, 'details'); $this->assertSame('app_dummy_details', $result); } public function testItThrowsExceptionWhenOperationHasNoResourceWithShortName(): void { $operation = new Show(shortName: 'custom_show'); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('No resource was found on the operation "custom_show"'); $this->operationRouteNameFactory->createRouteName($operation); } public function testItThrowsExceptionWhenOperationHasNoResourceWithDefaultShortName(): void { $operation = new Show(); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('No resource was found on the operation "show"'); $this->operationRouteNameFactory->createRouteName($operation); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/RoutePath/BulkOperationRoutePathFactoryTest.php ================================================ routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); $this->bulkOperationRoutePathFactory = new BulkOperationRoutePathFactory( $this->routePathFactory, new DashPathSegmentNameGenerator(new Inflector()), ); } public function testItGeneratesRoutePathForBulkDeleteOperations(): void { $operation = new BulkDelete(); $result = $this->bulkOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/bulk-delete', $result); } public function testItGeneratesRoutePathForBulkUpdateOperations(): void { $operation = new BulkUpdate(); $result = $this->bulkOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/bulk-update', $result); } public function testItDelegatesToDecoratedFactoryForNonBulkOperations(): void { $operation = new Index(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, '/dummies') ->willReturn('/dummies'); $result = $this->bulkOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies', $result); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/RoutePath/CollectionOperationRoutePathFactoryTest.php ================================================ routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); $this->collectionOperationRoutePathFactory = new CollectionOperationRoutePathFactory( $this->routePathFactory, new DashPathSegmentNameGenerator(new Inflector()), ); } public function testItGeneratesRoutePathForIndexOperations(): void { $operation = new Index(); $result = $this->collectionOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies', $result); } public function testItGeneratesRoutePathForIndexOperationsWithCustomShortName(): void { $operation = new Index(shortName: 'list'); $result = $this->collectionOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/list', $result); } public function testItGeneratesRoutePathForApiGetCollectionOperations(): void { $operation = new Api\GetCollection(); $result = $this->collectionOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies', $result); } public function testItDelegatesToDecoratedFactoryForNonCollectionOperations(): void { $operation = new Show(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, '/dummies') ->willReturn('/dummies/{id}'); $result = $this->collectionOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}', $result); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/RoutePath/CreateOperationRoutePathFactoryTest.php ================================================ routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); $this->createOperationRoutePathFactory = new CreateOperationRoutePathFactory( $this->routePathFactory, new DashPathSegmentNameGenerator(new Inflector()), ); } public function testItGeneratesRoutePathForCreateOperations(): void { $operation = new Create(); $result = $this->createOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/new', $result); } public function testItGeneratesRoutePathForCreateOperationsWithCustomShortName(): void { $operation = new Create(shortName: 'register'); $result = $this->createOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/register', $result); } public function testItGeneratesRoutePathForApiPostOperations(): void { $operation = new Api\Post(); $result = $this->createOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies', $result); } public function testItDelegatesToDecoratedFactoryForNonCreateOperations(): void { $operation = new Index(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, '/dummies') ->willReturn('/dummies'); $result = $this->createOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies', $result); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/RoutePath/DeleteOperationRoutePathFactoryTest.php ================================================ routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); $this->deleteOperationRoutePathFactory = new DeleteOperationRoutePathFactory( $this->routePathFactory, new DashPathSegmentNameGenerator(new Inflector()), ); } public function testItGeneratesRoutePathForDeleteOperations(): void { $operation = new Delete(); $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}/delete', $result); } public function testItGeneratesRoutePathForDeleteOperationsWithCustomIdentifier(): void { $operation = (new Delete())->withResource(new ResourceMetadata(identifier: 'code')); $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{code}/delete', $result); } public function testItGeneratesRoutePathForDeleteOperationsWithCustomShortName(): void { $operation = new Delete(shortName: 'remove'); $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}/remove', $result); } public function testItGeneratesRoutePathForApiDeleteOperations(): void { $operation = new Api\Delete(); $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}', $result); } public function testItGeneratesRoutePathForApiDeleteOperationsWithCustomShortName(): void { $operation = new Api\Delete(shortName: 'remove'); $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}/remove', $result); } public function testItDelegatesToDecoratedFactoryForNonDeleteOperations(): void { $operation = new Show(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, '/dummies') ->willReturn('/dummies/{id}'); $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}', $result); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/RoutePath/OperationRoutePathFactoryTest.php ================================================ operationRoutePathFactory = new OperationRoutePathFactory(); } public function testItThrowsExceptionWhenCalledWithOperationWithName(): void { $operation = new Show(name: 'app_dummy_show'); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Impossible to get a default route path for operation "app_dummy_show". Please define a path.'); $this->operationRoutePathFactory->createRoutePath($operation, '/dummies'); } public function testItThrowsExceptionWhenCalledWithOperationWithoutName(): void { $operation = new Show(); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Impossible to get a default route path for operation "". Please define a path.'); $this->operationRoutePathFactory->createRoutePath($operation, '/dummies'); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/RoutePath/ShowOperationRoutePathFactoryTest.php ================================================ routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); $this->showOperationRoutePathFactory = new ShowOperationRoutePathFactory( $this->routePathFactory, new DashPathSegmentNameGenerator(new Inflector()), ); } public function testItGeneratesRoutePathForShowOperations(): void { $operation = new Show(); $result = $this->showOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}', $result); } public function testItGeneratesRoutePathForShowOperationsWithCustomIdentifier(): void { $operation = (new Show())->withResource(new ResourceMetadata(identifier: 'code')); $result = $this->showOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{code}', $result); } public function testItGeneratesRoutePathForShowOperationsWithCustomShortName(): void { $operation = new Show(shortName: 'details'); $result = $this->showOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}/details', $result); } public function testItGeneratesRoutePathForApiGetOperations(): void { $operation = new Api\Get(); $result = $this->showOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}', $result); } public function testItDelegatesToDecoratedFactoryForNonShowOperations(): void { $operation = new Index(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, '/dummies') ->willReturn('/dummies'); $result = $this->showOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies', $result); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Factory/RoutePath/UpdateOperationRoutePathFactoryTest.php ================================================ routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); $this->updateOperationRoutePathFactory = new UpdateOperationRoutePathFactory( $this->routePathFactory, new DashPathSegmentNameGenerator(new Inflector()), ); } public function testItGeneratesRoutePathForUpdateOperations(): void { $operation = new Update(); $result = $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}/edit', $result); } public function testItGeneratesRoutePathForUpdateOperationsWithCustomIdentifier(): void { $operation = (new Update())->withResource(new ResourceMetadata(identifier: 'code')); $result = $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{code}/edit', $result); } public function testItGeneratesRoutePathForUpdateOperationsWithCustomShortName(): void { $operation = new Update(shortName: 'edition'); $result = $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}/edition', $result); } public function testItGeneratesRoutePathForApiPutOperations(): void { $operation = new Api\Put(); $result = $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}', $result); } public function testItGeneratesRoutePathForApiPatchOperations(): void { $operation = new Api\Patch(); $result = $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}', $result); } public function testItDelegatesToDecoratedFactoryForNonUpdateOperations(): void { $operation = new Show(); $this->routePathFactory ->expects($this->once()) ->method('createRoutePath') ->with($operation, '/dummies') ->willReturn('/dummies/{id}'); $result = $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies'); $this->assertSame('/dummies/{id}', $result); } } ================================================ FILE: src/Component/tests/Symfony/Routing/Loader/ResourceLoaderTest.php ================================================ resourceClassListFactory = $this->createMock(ResourceClassListFactoryInterface::class); $this->resourceRouteCollectionFactory = $this->createMock(ResourceRouteCollectionFactoryInterface::class); $this->loader = new ResourceLoader( $this->resourceClassListFactory, $this->resourceRouteCollectionFactory, ); } public function testItIsARouteLoader(): void { $this->assertInstanceOf(RouteLoaderInterface::class, $this->loader); } public function testItGeneratesRoutesFromResource(): void { $routeCollection = new RouteCollection(); $routeCollection->add('first_route', new Route('/first-route')); $routeCollection->add('second_route', new Route('/second-route')); $resourceClassList = new ResourceClassList(['\DummyClass']); $this->resourceClassListFactory->method('create')->willReturn($resourceClassList); $this->resourceRouteCollectionFactory->method('createRouteCollectionForClass')->with('\DummyClass')->willReturn($routeCollection); $this->assertEquals($routeCollection, ($this->loader)()); } } ================================================ FILE: src/Component/tests/Symfony/Routing/RedirectHandlerTest.php ================================================ router = $this->createMock(RouterInterface::class); $this->argumentParser = $this->createMock(ArgumentParserInterface::class); $this->operationRouteNameFactory = $this->createMock(OperationRouteNameFactoryInterface::class); $this->filterStorage = $this->createMock(FilterStorageInterface::class); $this->redirectHandler = new RedirectHandler( $this->router, $this->argumentParser, $this->operationRouteNameFactory, $this->filterStorage, ); } private function createDataWithId(string $id = 'xyz'): \stdClass { $data = new \stdClass(); $data->id = $id; return $data; } private function createDataWithCode(string $code = 'xyz'): \stdClass { $data = new \stdClass(); $data->code = $code; return $data; } private function mockFilterStorage(array $filters = []): void { $this->filterStorage->method('all')->willReturn($filters); } private function mockRouter(string $route, array $parameters, string $returnUrl): void { $this->router ->expects($this->once()) ->method('generate') ->with($route, $parameters) ->willReturn($returnUrl); } public function testItRedirectsToResourceWithIdArgumentByDefault(): void { $data = $this->createDataWithId(); $operation = (new Create(redirectToRoute: 'app_dummy_index')) ->withResource(new ResourceMetadata(alias: 'app.book')); $request = $this->createMock(Request::class); $this->mockFilterStorage(); $this->mockRouter('app_dummy_index', ['id' => 'xyz'], '/dummies'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItRedirectsToResourceWithCustomIdentifierArgumentByDefault(): void { $data = $this->createDataWithCode(); $operation = (new Create(redirectToRoute: 'app_dummy_index')) ->withResource(new ResourceMetadata(alias: 'app.ok', identifier: 'code')); $request = $this->createMock(Request::class); $this->mockFilterStorage(); $this->mockRouter('app_dummy_index', ['code' => 'xyz'], '/dummies'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItRedirectsToResourceWithoutArgumentsAfterDeleteOperationByDefault(): void { $data = $this->createDataWithId(); $operation = (new Delete(redirectToRoute: 'app_dummy_index')) ->withResource(new ResourceMetadata(alias: 'app.book')); $request = $this->createMock(Request::class); $this->mockFilterStorage(); $this->mockRouter('app_dummy_index', [], '/dummies'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItUsesFiltersFromGridStorageWhenRedirectingToAnIndexOperation(): void { $data = $this->createDataWithId(); $operation = (new Delete(redirectToRoute: 'app_dummy_index')) ->withResource(new ResourceMetadata(alias: 'app.book')); $request = $this->createMock(Request::class); $this->mockFilterStorage(['criteria' => ['enabled' => true]]); $this->mockRouter('app_dummy_index', ['criteria' => ['enabled' => true]], '/dummies'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItRedirectsToRoute(): void { $data = new \stdClass(); $this->mockFilterStorage(); $this->mockRouter('app_dummy_index', [], '/dummies'); $this->redirectHandler->redirectToRoute($data, 'app_dummy_index'); } public function testItThrowsAnExceptionWhenOperationHasNoResource(): void { $data = new \stdClass(); $operation = new Create(redirectToRoute: 'app_dummy_index', name: 'app_dummy_create'); $request = $this->createMock(Request::class); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Operation "app_dummy_create" has no resource, but it should.'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItThrowsAnExceptionWhenOperationHasNoRouteRedirection(): void { $data = new \stdClass(); $operation = new Create(name: 'app_dummy_create'); $request = $this->createMock(Request::class); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Operation "app_dummy_create" has no redirection route, but it should.'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItThrowsAnExceptionWhenTryingRedirectWithCustomArgumentsThatAreNotScalarOnes(): void { $data = new \stdClass(); $data->code = 'xyz'; $operation = new Create( redirectToRoute: 'app_dummy_index', redirectArguments: ['code' => 'resource.code', 'criteria' => ['foo' => 'resource.code', 'bar' => new \stdClass()]], ); $resource = new ResourceMetadata(alias: 'app.book'); $operation = $operation->withResource($resource); $request = $this->createMock(Request::class); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Parameter "bar" should be a scalar or an array.'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItRedirectsToOperationWithIdArgumentByDefault(): void { $data = $this->createDataWithId(); $operation = (new Create(name: 'app_dummy_create')) ->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $request = $this->createMock(Request::class); $this->operationRouteNameFactory ->expects($this->once()) ->method('createRouteName') ->with($operation, 'show') ->willReturn('app_dummy_show'); $this->mockFilterStorage(); $this->mockRouter('app_dummy_show', ['id' => 'xyz'], '/dummies/xyz'); $response = $this->redirectHandler->redirectToOperation($data, $operation, $request, 'show'); $this->assertSame('/dummies/xyz', $response->getTargetUrl()); $this->assertSame(302, $response->getStatusCode()); } public function testItRedirectsToOperationWithCustomIdentifierArgumentByDefault(): void { $data = $this->createDataWithCode('ABC123'); $operation = (new Create(name: 'app_product_create')) ->withResource(new ResourceMetadata(alias: 'app.product', name: 'product', applicationName: 'app', identifier: 'code')); $request = $this->createMock(Request::class); $this->operationRouteNameFactory ->expects($this->once()) ->method('createRouteName') ->with($operation, 'update') ->willReturn('app_product_update'); $this->mockFilterStorage(); $this->mockRouter('app_product_update', ['code' => 'ABC123'], '/products/ABC123/edit'); $response = $this->redirectHandler->redirectToOperation($data, $operation, $request, 'update'); $this->assertSame('/products/ABC123/edit', $response->getTargetUrl()); $this->assertSame(302, $response->getStatusCode()); } public function testItRedirectsToOperationForDeleteWithoutArgumentsByDefault(): void { $data = $this->createDataWithId(); $operation = (new Delete(name: 'app_dummy_delete')) ->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $request = $this->createMock(Request::class); $this->operationRouteNameFactory ->expects($this->once()) ->method('createRouteName') ->with($operation, 'index') ->willReturn('app_dummy_index'); $this->mockFilterStorage(); $this->mockRouter('app_dummy_index', [], '/dummies'); $response = $this->redirectHandler->redirectToOperation($data, $operation, $request, 'index'); $this->assertSame('/dummies', $response->getTargetUrl()); $this->assertSame(302, $response->getStatusCode()); } public function testItRedirectsToOperationWithCustomRedirectArguments(): void { $data = new \stdClass(); $data->id = 'xyz'; $data->slug = 'my-book'; $operation = new Create( name: 'app_book_create', redirectArguments: ['slug' => 'resource.slug'], ); $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); $operation = $operation->withResource($resource); $request = $this->createMock(Request::class); $this->operationRouteNameFactory ->expects($this->once()) ->method('createRouteName') ->with($operation, 'show') ->willReturn('app_book_show'); $this->filterStorage->method('all')->willReturn([]); $this->router ->expects($this->once()) ->method('generate') ->with('app_book_show', ['slug' => 'my-book']) ->willReturn('/books/my-book'); $response = $this->redirectHandler->redirectToOperation($data, $operation, $request, 'show'); $this->assertSame('/books/my-book', $response->getTargetUrl()); } public function testItThrowsExceptionWhenOperationHasNoResourceInRedirectToOperation(): void { $data = new \stdClass(); $operation = new Create(name: 'app_dummy_create'); $request = $this->createMock(Request::class); $this->operationRouteNameFactory ->method('createRouteName') ->willReturn('app_dummy_show'); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Operation "app_dummy_create" has no resource, but it should.'); $this->redirectHandler->redirectToOperation($data, $operation, $request, 'show'); } public function testItParsesNestedArrayArguments(): void { $data = $this->createDataWithId(); $data->code = 'ABC'; $operation = (new Create( redirectToRoute: 'app_dummy_index', redirectArguments: [ 'id' => 'resource.id', 'criteria' => [ 'code' => 'resource.code', 'enabled' => true, ], ], ))->withResource(new ResourceMetadata(alias: 'app.book')); $request = $this->createMock(Request::class); $this->mockFilterStorage(); $this->mockRouter('app_dummy_index', [ 'id' => 'xyz', 'criteria' => [ 'code' => 'ABC', 'enabled' => true, ], ], '/dummies'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItParsesResourcePropertyPathWithPropertyAccessor(): void { $data = $this->createDataWithId(); $data->author = new \stdClass(); $data->author->name = 'John Doe'; $operation = (new Create( redirectToRoute: 'app_book_show', redirectArguments: ['id' => 'resource.id', 'author' => 'resource.author.name'], ))->withResource(new ResourceMetadata(alias: 'app.book')); $request = $this->createMock(Request::class); $this->mockFilterStorage(); $this->mockRouter('app_book_show', ['id' => 'xyz', 'author' => 'John Doe'], '/books/xyz'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItUsesExpressionParserForStringValuesWithoutResourcePrefix(): void { $data = $this->createDataWithId(); $operation = (new Create( redirectToRoute: 'app_book_show', redirectArguments: ['id' => 'resource.id', 'page' => "request.query.get('page')"], ))->withResource(new ResourceMetadata(alias: 'app.book')); // name is null $request = $this->createMock(Request::class); $this->argumentParser ->expects($this->once()) ->method('parseExpression') ->with("request.query.get('page')", ['resource' => $data]) // Only 'resource', no name ->willReturn('1'); $this->mockFilterStorage(); $this->router ->method('generate') ->with('app_book_show', ['id' => 'xyz', 'page' => '1']) ->willReturn('/books/xyz?page=1'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItUsesExpressionParserForExpressionLanguagePrefixedValues(): void { $data = $this->createDataWithId(); $operation = (new Create( redirectToRoute: 'app_book_show', redirectArguments: ['id' => '@resource.getId()', 'page' => "@=request.query.get('page')"], ))->withResource(new ResourceMetadata(alias: 'app.book')); // name is null $request = $this->createMock(Request::class); $this->argumentParser ->expects($this->exactly(2)) ->method('parseExpression') ->willReturnCallback(function (string $expression) { if ('@resource.getId()' === $expression) { return 'xyz'; } if ("request.query.get('page')" === $expression) { return '1'; } return null; }) ; $this->mockFilterStorage(); $this->router ->method('generate') ->with('app_book_show', ['id' => 'xyz', 'page' => '1']) ->willReturn('/books/xyz?page=1'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItSkipsExpressionParserForNonStringScalarValues(): void { $data = $this->createDataWithId(); $operation = (new Create( redirectToRoute: 'app_book_show', redirectArguments: ['id' => 'resource.id', 'page' => 1, 'active' => true], ))->withResource(new ResourceMetadata(alias: 'app.book')); $request = $this->createMock(Request::class); $this->argumentParser ->expects($this->never()) ->method('parseExpression'); $this->mockFilterStorage(); $this->mockRouter('app_book_show', ['id' => 'xyz', 'page' => 1, 'active' => true], '/books/xyz'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItUsesExpressionParserWhenPropertyAccessorCannotReadProperty(): void { $data = $this->createDataWithId(); $operation = (new Create( redirectToRoute: 'app_book_show', redirectArguments: ['id' => 'resource.id', 'nonExistent' => 'resource.nonExistentProperty'], ))->withResource(new ResourceMetadata(alias: 'app.book', name: 'book')); $request = $this->createMock(Request::class); $this->argumentParser ->expects($this->once()) ->method('parseExpression') ->with('resource.nonExistentProperty', ['resource' => $data, 'book' => $data]) ->willReturn('default_value'); $this->mockFilterStorage(); $this->mockRouter('app_book_show', ['id' => 'xyz', 'nonExistent' => 'default_value'], '/books/xyz'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItAddsResourceNameToVariablesWhenResourceNameIsSet(): void { $data = $this->createDataWithId(); $operation = (new Create( redirectToRoute: 'app_book_show', redirectArguments: ['id' => 'resource.id', 'type' => 'hardcover'], ))->withResource(new ResourceMetadata(alias: 'app.book', name: 'book')); $request = $this->createMock(Request::class); $this->argumentParser ->expects($this->once()) ->method('parseExpression') ->with('hardcover', ['resource' => $data, 'book' => $data]) ->willReturn('hardcover'); $this->mockFilterStorage(); $this->router ->method('generate') ->willReturn('/books/xyz'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItDoesNotAddResourceNameToVariablesWhenResourceNameIsNull(): void { $data = $this->createDataWithId(); $operation = (new Create( redirectToRoute: 'app_book_show', redirectArguments: ['id' => 'resource.id', 'type' => 'hardcover'], ))->withResource(new ResourceMetadata(alias: 'app.book')); // name is null $request = $this->createMock(Request::class); $this->argumentParser ->expects($this->once()) ->method('parseExpression') ->with('hardcover', ['resource' => $data]) ->willReturn('hardcover'); $this->mockFilterStorage(); $this->router ->method('generate') ->willReturn('/books/xyz'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItHandlesArrayDataWithResourcePrefix(): void { $data = ['id' => 'xyz', 'title' => 'My Book']; $operation = (new Create( redirectToRoute: 'app_book_show', redirectArguments: ['id' => 'resource.id', 'title' => 'resource.title'], ))->withResource(new ResourceMetadata(alias: 'app.book')); $request = $this->createMock(Request::class); $this->argumentParser ->expects($this->exactly(2)) ->method('parseExpression') ->willReturnCallback(function ($value, $variables) use ($data) { if ($value === 'resource.id') { return 'xyz'; } if ($value === 'resource.title') { return 'My Book'; } return $value; }); $this->mockFilterStorage(); $this->mockRouter('app_book_show', ['id' => 'xyz', 'title' => 'My Book'], '/books/xyz'); $this->redirectHandler->redirectToResource($data, $operation, $request); } public function testItRedirectsToRefererWhenHeaderIsPresent(): void { $data = new \stdClass(); $operation = (new Create(redirectTo: 'referer', redirectToRoute: 'app_dummy_index')) ->withResource(new ResourceMetadata(alias: 'app.book')); $request = new Request(); $request->headers->set('referer', '/previous-page'); // router should never be called $this->router ->expects($this->never()) ->method('generate') ; $response = $this->redirectHandler->redirectToResource($data, $operation, $request); $this->assertSame('/previous-page', $response->getTargetUrl()); } public function testItFallsBackToRouteWhenRefererIsMissing(): void { $data = new \stdClass(); $operation = (new Create(redirectTo: 'referer', redirectToRoute: 'app_dummy_index')) ->withResource(new ResourceMetadata(alias: 'app.book')); $request = new Request(); // No referer $this->router ->expects($this->once()) ->method('generate') ->willReturn('/fallback') ; $response = $this->redirectHandler->redirectToResource($data, $operation, $request); $this->assertSame('/fallback', $response->getTargetUrl()); } public function testItFallsBackToRouteWhenRefererIsExternal(): void { $data = new \stdClass(); $operation = (new Create(redirectTo: 'referer', redirectToRoute: 'app_dummy_index')) ->withResource(new ResourceMetadata(alias: 'app.book')); $request = new Request(); $request->headers->set('referer', 'https://malicious.com/'); $this->router ->expects($this->once()) ->method('generate') ->willReturn('/fallback') ; $response = $this->redirectHandler->redirectToResource($data, $operation, $request); $this->assertSame('/fallback', $response->getTargetUrl()); } } ================================================ FILE: src/Component/tests/Symfony/Security/OperationAccessCheckerTest.php ================================================ createMock(TokenInterface::class); $token->method('getRoleNames')->willReturn($roleNames); return $token; } private function createOperationMock(?string $securityExpression = 'is_granted("ROLE_ADMIN")'): Operation { $operation = $this->createMock(Operation::class); $operation->method('getSecurity')->willReturn($securityExpression); return $operation; } private function createExpressionLanguageMock(string $expression, bool $result): ExpressionLanguage { $expressionLanguage = $this->createMock(ExpressionLanguage::class); $expressionLanguage ->method('evaluate') ->with($expression, $this->isType('array')) ->willReturn($result); return $expressionLanguage; } #[DataProvider('getGranted')] public function testIsGranted(bool $granted): void { $expressionLanguage = $this->createExpressionLanguageMock('is_granted("ROLE_ADMIN")', $granted); $authenticationTrustResolver = $this->createMock(AuthenticationTrustResolverInterface::class); $token = $this->createTokenMock([]); $tokenStorage = $this->createMock(TokenStorageInterface::class); $tokenStorage->method('getToken')->willReturn($token); $operation = $this->createOperationMock(); $checker = new OperationAccessChecker( $expressionLanguage, $authenticationTrustResolver, null, $tokenStorage, ); $this->assertSame($granted, $checker->isGranted($operation, new Context())); } public function testSecurityComponentNotAvailable(): void { $this->expectException(\LogicException::class); $this->expectExceptionMessage('The "symfony/security" library must be installed to use the "security" attribute.'); $checker = new OperationAccessChecker( $this->createMock(ExpressionLanguage::class), ); $checker->isGranted(new HttpOperation(), new Context()); } public function testExpressionLanguageNotInstalled(): void { $this->expectException(\LogicException::class); $this->expectExceptionMessage('The "symfony/expression-language" library must be installed to use the "security" attribute.'); $authenticationTrustResolver = $this->createMock(AuthenticationTrustResolverInterface::class); $tokenStorage = $this->createMock(TokenStorageInterface::class); $tokenStorage->method('getToken')->willReturn($this->createMock(TokenInterface::class)); $checker = new OperationAccessChecker( null, $authenticationTrustResolver, null, $tokenStorage, ); $checker->isGranted(new HttpOperation(), new Context()); } public function testWithoutAuthenticationToken(): void { $expressionLanguage = $this->createExpressionLanguageMock('is_granted("ROLE_ADMIN")', true); $authenticationTrustResolver = $this->createMock(AuthenticationTrustResolverInterface::class); $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); $tokenStorage = $this->createMock(TokenStorageInterface::class); $tokenStorage->method('getToken')->willReturn(null); $operation = $this->createOperationMock(); $checker = new OperationAccessChecker( $expressionLanguage, $authenticationTrustResolver, null, $tokenStorage, $authorizationChecker, ); self::assertTrue($checker->isGranted($operation, new Context())); } public function testItGrantsAccessWhenOperationHasNoSecurityExpression(): void { $expressionLanguage = $this->createMock(ExpressionLanguage::class); // Expression language should not be called when security is null $expressionLanguage->expects($this->never())->method('evaluate'); $authenticationTrustResolver = $this->createMock(AuthenticationTrustResolverInterface::class); $tokenStorage = $this->createMock(TokenStorageInterface::class); $operation = $this->createOperationMock(null); $checker = new OperationAccessChecker( $expressionLanguage, $authenticationTrustResolver, null, $tokenStorage, ); // When security expression is null, should return true (access granted) self::assertTrue($checker->isGranted($operation, new Context())); } #[DataProvider('getGranted')] public function testIsGrantedWithRoleHierarchy(bool $granted): void { $expressionLanguage = $this->createExpressionLanguageMock('is_granted("ROLE_ADMIN")', $granted); $authenticationTrustResolver = $this->createMock(AuthenticationTrustResolverInterface::class); $token = $this->createTokenMock(['ROLE_USER']); $tokenStorage = $this->createMock(TokenStorageInterface::class); $tokenStorage->method('getToken')->willReturn($token); $roleHierarchy = $this->createMock(RoleHierarchyInterface::class); $roleHierarchy ->expects($this->once()) ->method('getReachableRoleNames') ->with(['ROLE_USER']) ->willReturn(['ROLE_USER', 'ROLE_ADMIN']); $operation = $this->createOperationMock(); $checker = new OperationAccessChecker( $expressionLanguage, $authenticationTrustResolver, $roleHierarchy, $tokenStorage, ); $this->assertSame($granted, $checker->isGranted($operation, new Context())); } } ================================================ FILE: src/Component/tests/Symfony/Serializer/State/DeserializeProviderTest.php ================================================ decorated = $this->createMock(ProviderInterface::class); $this->serializer = $this->createMock(SerializerInterface::class); $this->deserializableProvider = new DeserializeProvider( $this->decorated, $this->serializer, ); } private function createRequestMock(bool $isMethodSafe = false, string $format = 'json', mixed $content = ['food' => 'fighters'], string $method = 'POST'): Request { $request = $this->createMock(Request::class); $request->attributes = new ParameterBag(); $request->method('isMethodSafe')->willReturn($isMethodSafe); $request->method('getRequestFormat')->willReturn($format); $request->method('getContent')->willReturn($content); $request->method('getMethod')->willReturn($method); return $request; } private function createOperationMock( ResourceMetadata|false|null $resource = false, ?bool $canDeserialize = null, ?array $denormalizationContext = [], ): HttpOperation { $operation = $this->createMock(HttpOperation::class); if ($resource === false) { $operation->method('getResource')->willReturn(new ResourceMetadata(alias: 'app.dummy', class: 'App\Resource')); } else { $operation->method('getResource')->willReturn($resource); } $operation->method('canDeserialize')->willReturn($canDeserialize); $operation->method('getDenormalizationContext')->willReturn($denormalizationContext); return $operation; } public function testItDeserializesData(): void { $request = $this->createRequestMock(); $operation = $this->createOperationMock(); $data = new \stdClass(); $context = new Context(new RequestOption($request)); $this->decorated->expects($this->once())->method('provide')->with($operation, $context)->willReturn($data); $this->serializer->expects($this->once())->method('deserialize')->with(['food' => 'fighters'], 'App\Resource', 'json', ['object_to_populate' => $data])->willReturn($data); $this->deserializableProvider->provide($operation, $context); } public function testItDeserializesDataWithDenormalizationContext(): void { $request = $this->createRequestMock(); $operation = $this->createOperationMock(denormalizationContext: ['groups' => ['dummy:write']]); $data = new \stdClass(); $context = new Context(new RequestOption($request)); $this->serializer->expects($this->once())->method('deserialize')->with(['food' => 'fighters'], 'App\Resource', 'json', ['groups' => ['dummy:write']])->willReturn($data); $this->deserializableProvider->provide($operation, $context); } public function testItDoesNothingIfOperationCannotBeDeserialized(): void { $request = $this->createRequestMock(); $operation = $this->createOperationMock(canDeserialize: false); $context = new Context(new RequestOption($request)); $this->serializer->expects($this->never())->method('deserialize'); $this->deserializableProvider->provide($operation, $context); } public function testItDoesNothingIfOperationHasNoResource(): void { $request = $this->createRequestMock(); $operation = $this->createOperationMock(resource: null, canDeserialize: true); $context = new Context(new RequestOption($request)); $data = new \stdClass(); $this->decorated->method('provide')->with($operation, $context)->willReturn($data); $this->serializer->expects($this->never())->method('deserialize'); $result = $this->deserializableProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItDoesNothingIfRequestFormatIsHtml(): void { $request = $this->createRequestMock(format: 'html'); $operation = $this->createOperationMock(canDeserialize: true); $context = new Context(new RequestOption($request)); $this->serializer->expects($this->never())->method('deserialize'); $this->deserializableProvider->provide($operation, $context); } public function testItDoesNothingIfRequestMethodIsSafe(): void { $request = $this->createRequestMock(isMethodSafe: true); $operation = $this->createOperationMock(canDeserialize: true); $context = new Context(new RequestOption($request)); $this->serializer->expects($this->never())->method('deserialize'); $this->deserializableProvider->provide($operation, $context); } public function testItDoesNothingIfOperationIsADeleteOne(): void { $request = $this->createRequestMock(); $operation = (new Delete())->withResource(new ResourceMetadata(alias: 'app.dummy', class: 'App\Resource')); $context = new Context(new RequestOption($request)); $this->serializer->expects($this->never())->method('deserialize'); $this->deserializableProvider->provide($operation, $context); } public function testItThrowsAnExceptionWhenSerializerIsNotAvailable(): void { $request = $this->createRequestMock(); $operation = $this->createOperationMock(canDeserialize: true); $context = new Context(new RequestOption($request)); $this->expectException(\LogicException::class); $this->expectExceptionMessage('You can not use the "json" format if the Serializer is not available. Try running "composer require symfony/serializer".'); $deserializableProvider = new DeserializeProvider($this->decorated, null); $deserializableProvider->provide($operation, $context); } public function testItReturnsDataWhenOperationIsNotHttpOperation(): void { $operation = $this->createMock(\Sylius\Resource\Metadata\Operation::class); $data = new \stdClass(); $context = new Context(); $this->decorated->expects($this->once())->method('provide')->with($operation, $context)->willReturn($data); $this->serializer->expects($this->never())->method('deserialize'); $result = $this->deserializableProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItReturnsDataWhenRequestIsNull(): void { $operation = $this->createMock(HttpOperation::class); $data = new \stdClass(); $context = new Context(); $this->decorated->expects($this->once())->method('provide')->with($operation, $context)->willReturn($data); $this->serializer->expects($this->never())->method('deserialize'); $result = $this->deserializableProvider->provide($operation, $context); $this->assertSame($data, $result); } } ================================================ FILE: src/Component/tests/Symfony/Serializer/State/SerializeProcessorTest.php ================================================ processor = $this->createMock(ProcessorInterface::class); $this->serializer = $this->createMock(SerializerInterface::class); $this->serializeProcessor = new SerializeProcessor( $this->processor, $this->serializer, ); } private function createRequestMock(string $format = 'json'): Request { $request = $this->createMock(Request::class); $request->method('getRequestFormat')->willReturn($format); return $request; } private function createOperationMock(?bool $canSerialize = null, array $normalizationContext = []): HttpOperation { $operation = $this->createMock(HttpOperation::class); $operation->method('canSerialize')->willReturn($canSerialize); $operation->method('getNormalizationContext')->willReturn($normalizationContext); return $operation; } public function testItSerializesDataToTheRequestedFormat(): void { $request = $this->createRequestMock(); $operation = $this->createOperationMock(); $data = new \stdClass(); $context = new Context(new RequestOption($request)); $this->processor->expects($this->once())->method('process')->with('serialized_data', $operation, $context)->willReturn('serialized_data'); $this->serializer->expects($this->once())->method('serialize')->with($data, 'json', [])->willReturn('serialized_data'); $result = $this->serializeProcessor->process($data, $operation, $context); $this->assertSame('serialized_data', $result); } public function testItSerializesDataToTheRequestedFormatWithNormalizationContext(): void { $request = $this->createRequestMock(); $operation = $this->createOperationMock(normalizationContext: ['groups' => ['dummy:read']]); $data = new \stdClass(); $context = new Context(new RequestOption($request)); $this->processor->expects($this->once())->method('process')->with('serialized_data', $operation, $context)->willReturn('serialized_data'); $this->serializer->expects($this->once())->method('serialize')->with($data, 'json', ['groups' => ['dummy:read']])->willReturn('serialized_data'); $result = $this->serializeProcessor->process($data, $operation, $context); $this->assertSame('serialized_data', $result); } public function testItDoesNothingWhenFormatIsHtml(): void { $request = $this->createRequestMock('html'); $operation = $this->createOperationMock(); $data = new \stdClass(); $context = new Context(new RequestOption($request)); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($data); $this->serializer->expects($this->never())->method('serialize'); $result = $this->serializeProcessor->process($data, $operation, $context); $this->assertSame($data, $result); } public function testItThrowsAnExceptionWhenSerializerIsNotAvailable(): void { $request = $this->createRequestMock(); $operation = $this->createOperationMock(); $data = new \stdClass(); $context = new Context(new RequestOption($request)); $serializeProcessor = new SerializeProcessor($this->processor, null); $this->processor->expects($this->never())->method('process'); $this->expectException(\LogicException::class); $this->expectExceptionMessage('You can not use the "json" format if the Serializer is not available. Try running "composer require symfony/serializer".'); $serializeProcessor->process($data, $operation, $context); } public function testItDoesNothingIfOperationCannotBeSerialized(): void { $request = $this->createRequestMock(); $operation = $this->createOperationMock(canSerialize: false); $data = new \stdClass(); $context = new Context(new RequestOption($request)); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($data); $this->serializer->expects($this->never())->method('serialize'); $result = $this->serializeProcessor->process($data, $operation, $context); $this->assertSame($data, $result); } public function testItProcessesDataWithoutSerializationWhenRequestIsNull(): void { $operation = $this->createOperationMock(); $data = new \stdClass(); $context = new Context(); $this->processor->expects($this->once())->method('process')->with($data, $operation, $context)->willReturn($data); $this->serializer->expects($this->never())->method('serialize'); $result = $this->serializeProcessor->process($data, $operation, $context); $this->assertSame($data, $result); } } ================================================ FILE: src/Component/tests/Symfony/Session/Flash/FlashHelperTest.php ================================================ setSession($session); $operation = (new Create())->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper = new FlashHelper($this->createTranslator()); $flashHelper->addSuccessFlash($operation, $context, 'Custom message.'); $this->assertSame('Custom message.', $session->getFlashBag()->all()['success'][0]); } public function testItAddsSuccessFlashesWithSpecificMessage(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'app.dummy.create' => '%resource% was created successfully!!', 'sylius.resource.create' => '%resource% was created successfully.', ]); $flashHelper = new FlashHelper($translator); $operation = (new Create())->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addSuccessFlash($operation, $context); $this->assertSame('Dummy was created successfully!!', $session->getFlashBag()->all()['success'][0]); } public function testItAddsSuccessFlashesWithFallbackMessage(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'sylius.resource.create' => '%resource% was created successfully.', ]); $flashHelper = new FlashHelper($translator); $operation = (new Create())->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addSuccessFlash($operation, $context); $this->assertSame('Dummy was created successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItAddsSuccessFlashesWithSpecificMessageForCustomOperationName(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'app.dummy.create_for_customer' => '%resource% has been added to this customer successfully.', ]); $flashHelper = new FlashHelper($translator); $operation = (new Create(shortName: 'create_for_customer'))->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addSuccessFlash($operation, $context); $this->assertSame('Dummy has been added to this customer successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItAddsSuccessFlashesWithCustomMessageOnItsOperation(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'app.dummy.shipped' => '%resource% was shipped successfully.', ]); $flashHelper = new FlashHelper($translator); $operation = (new Create(notificationMessage: 'app.dummy.shipped'))->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addSuccessFlash($operation, $context); $this->assertSame('Dummy was shipped successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItAddsSuccessFlashesWithCustomMessageOnItsOperationEvenIfThisIsNotInTheTranslationBag(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator(); $flashHelper = new FlashHelper($translator); $operation = (new Create(notificationMessage: 'Dummy was shipped successfully.'))->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addSuccessFlash($operation, $context); $this->assertSame('Dummy was shipped successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItAddsSuccessFlashesWithFirstDefaultMessageWhenTranslatorIsNotABag(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $operation = (new Create())->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $translator = $this->createMock(TranslatorInterface::class); $flashHelper = new FlashHelper($translator); $translator->expects($this->once())->method('trans')->with('app.dummy.create', ['%resource%' => 'Dummy'], 'flashes')->willReturn('Dummy was created successfully.'); $flashHelper->addSuccessFlash($operation, $context); $this->assertSame('Dummy was created successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItAddsSuccessFlashesWithHumanizedMessage(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'app.admin_user.create' => '%resource% was created successfully.', ]); $flashHelper = new FlashHelper($translator); $operation = (new Create())->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'admin_user', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addSuccessFlash($operation, $context); $this->assertSame('Admin user was created successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItAddsSuccessFlashesWithTranslatedResourceInTheMessage(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'sylius.resource.create' => '%resource% was created successfully.', ], [ 'app.ui.promotion' => 'Cart promotion', ]); $flashHelper = new FlashHelper($translator); $operation = (new Create())->withResource(new ResourceMetadata(alias: 'app.promotion', name: 'promotion', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addSuccessFlash($operation, $context); $this->assertSame('Cart promotion was created successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItAddsSuccessFlashesWithHumanizedMessageAndPluralNameOnBulkOperation(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'app.admin_user.bulk_delete' => '%resources% was removed successfully.', ]); $flashHelper = new FlashHelper($translator); $operation = (new BulkDelete())->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'admin_user', pluralName: 'admin_users', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addSuccessFlash($operation, $context); $this->assertSame('Admin users was removed successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItAddsSuccessFlashesWithTranslatedResourceWithPluralNameInTheMessageOnBulkOperation(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'app.promotion.bulk_delete' => '%resources% was removed successfully.', ], [ 'app.ui.promotions' => 'Cart promotions', ]); $flashHelper = new FlashHelper($translator); $operation = (new BulkDelete())->withResource(new ResourceMetadata(alias: 'app.promotion', name: 'promotion', pluralName: 'promotions', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addSuccessFlash($operation, $context); $this->assertSame('Cart promotions was removed successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItAddsErrorFlashesWithCustomMessage(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $operation = (new Create())->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper = new FlashHelper($this->createTranslator()); $flashHelper->addErrorFlash($operation, $context, 'Custom error message.'); $this->assertSame('Custom error message.', $session->getFlashBag()->all()['error'][0]); } public function testItAddsErrorFlashesWithSpecificMessage(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'app.dummy.create_error' => 'Cannot create %resource% resource!!', 'sylius.resource.create_error' => 'Cannot create %resource% resource.', ]); $flashHelper = new FlashHelper($translator); $operation = (new Create())->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addErrorFlash($operation, $context); $this->assertSame('Cannot create Dummy resource!!', $session->getFlashBag()->all()['error'][0]); } public function testItAddsErrorFlashesWithFallbackMessage(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'sylius.resource.create_error' => 'Cannot create %resource% resource.', ]); $flashHelper = new FlashHelper($translator); $operation = (new Create())->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addErrorFlash($operation, $context); $this->assertSame('Cannot create Dummy resource.', $session->getFlashBag()->all()['error'][0]); } public function testItAddsErrorFlashesWithSpecificMessageForCustomOperationName(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'app.dummy.create_for_customer_error' => 'Cannot create %resource% resource for this customer.', ]); $flashHelper = new FlashHelper($translator); $operation = (new Create(shortName: 'create_for_customer'))->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addErrorFlash($operation, $context); $this->assertSame('Cannot create Dummy resource for this customer.', $session->getFlashBag()->all()['error'][0]); } public function testItAddsErrorFlashesWithFallbackMessageForCustomOperationName(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'sylius.resource.create_error' => 'Cannot create %resource% resource.', ]); $flashHelper = new FlashHelper($translator); $operation = (new Create(shortName: 'create_for_customer'))->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addErrorFlash($operation, $context); $this->assertSame('Cannot create Dummy resource.', $session->getFlashBag()->all()['error'][0]); } public function testItAddsErrorFlashesWithAFallbackToDeleteTranslationKeyForABulkDelete(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'sylius.resource.delete_error' => 'Cannot delete %resource% resource.', ]); $flashHelper = new FlashHelper($translator); $operation = (new BulkDelete())->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addErrorFlash($operation, $context); $this->assertSame('Cannot delete Dummy resource.', $session->getFlashBag()->all()['error'][0]); } public function testItAddsErrorFlashesWithAFallbackToDeleteTranslationKeyForABulkDeleteWithACustomOperationName(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $translator = $this->createTranslator([ 'sylius.resource.delete_error' => 'Cannot delete %resource% resource.', ]); $flashHelper = new FlashHelper($translator); $operation = (new BulkDelete(shortName: 'bulk_delete_for_taxon'))->withResource(new ResourceMetadata(alias: 'app.dummy', name: 'dummy', applicationName: 'app')); $context = new Context(new RequestOption($request)); $flashHelper->addErrorFlash($operation, $context); $this->assertSame('Cannot delete Dummy resource.', $session->getFlashBag()->all()['error'][0]); } public function testItTranslatesFlashesFromEventWhenTranslatorIsNotABag(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $event = new GenericEvent(); $event->setMessage('app.admin_user.banned'); $event->setMessageType('success'); $event->setMessageParameters(['%admin_user%' => 'Darth Vader']); $context = new Context(new RequestOption($request)); $translator = $this->createMock(TranslatorInterface::class); $flashHelper = new FlashHelper($translator); $translator->expects($this->once())->method('trans')->with('app.admin_user.banned', ['%admin_user%' => 'Darth Vader'], 'flashes')->willReturn('Darth Vader was banned successfully.'); $flashHelper->addFlashFromEvent($event, $context); $this->assertSame('Darth Vader was banned successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItTranslatesFlashesFromEventWhenTranslatorIsABag(): void { $session = new Session(); $request = new Request(); $request->setSession($session); $event = new GenericEvent(); $event->setMessage('app.admin_user.banned'); $event->setMessageType('success'); $event->setMessageParameters(['%admin_user%' => 'Darth Vader']); $translator = $this->createTranslator([ 'app.admin_user.banned' => '%admin_user% was banned successfully.', ]); $flashHelper = new FlashHelper($translator); $context = new Context(new RequestOption($request)); $flashHelper->addFlashFromEvent($event, $context); $this->assertSame('Darth Vader was banned successfully.', $session->getFlashBag()->all()['success'][0]); } public function testItDoesNotTranslateEventMessageWhenTranslatorIsABagAndDoesNotContainsTheKey(): void { $translator = $this->createMockForIntersectionOfInterfaces([TranslatorInterface::class, TranslatorBagInterface::class]); $messageCatalogue = $this->createMock(MessageCatalogueInterface::class); $session = new Session(); $request = new Request(); $request->setSession($session); $flashHelper = new FlashHelper($translator); $event = new GenericEvent(); $event->setMessage('Darth Vader was banned successfully.'); $event->setMessageType('success'); $context = new Context(new RequestOption($request)); $translator->method('getCatalogue')->willReturn($messageCatalogue); $messageCatalogue->expects($this->once())->method('has')->with('Darth Vader was banned successfully.', 'flashes')->willReturn(false); $translator->expects($this->never())->method('trans'); $flashHelper->addFlashFromEvent($event, $context); $this->assertSame('Darth Vader was banned successfully.', $session->getFlashBag()->all()['success'][0]); } /** * @param array $flashesTranslations * @param array $messagesTranslations */ private function createTranslator(array $flashesTranslations = [], array $messagesTranslations = []): TranslatorInterface&TranslatorBagInterface { $translator = new Translator('en'); $translator->addLoader('array', new ArrayLoader()); foreach ($flashesTranslations as $key => $translation) { $translator->addResource('array', [ $key => $translation, ], 'en', 'flashes'); } foreach ($messagesTranslations as $key => $translation) { $translator->addResource('array', [ $key => $translation, ], 'en', 'messages'); } return $translator; } } ================================================ FILE: src/Component/tests/Symfony/Validator/EventListener/ValidationExceptionListenerTest.php ================================================ serializer = $this->createMock(SerializerInterface::class); $this->validationExceptionListener = new ValidationExceptionListener($this->serializer); } public function testItIsInitializable(): void { $this->assertInstanceOf(ValidationExceptionListener::class, $this->validationExceptionListener); } public function testItTransformsValidationExceptionToAResponse(): void { $violationList = new ConstraintViolationList(); $exception = new ValidationException($violationList); $kernel = $this->createMock(KernelInterface::class); $request = $this->createMock(Request::class); $event = new ExceptionEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $exception); $request->method('getRequestFormat')->willReturn('json'); $request->method('getMimeType')->with('json')->willReturn('application/json'); $this->serializer ->expects($this->once()) ->method('serialize') ->with($violationList, 'json') ->willReturn('serialized_exception'); $this->validationExceptionListener->onKernelException($event); $response = $event->getResponse(); $this->assertInstanceOf(Response::class, $response); $this->assertSame('serialized_exception', $response->getContent()); $this->assertSame(422, $response->getStatusCode()); $this->assertSame('application/json; charset=utf-8', $response->headers->get('Content-Type')); $this->assertSame('nosniff', $response->headers->get('X-Content-Type-Options')); $this->assertSame('deny', $response->headers->get('X-Frame-Options')); } public function testItDoesNothingOnOtherExceptions(): void { $exception = new \Exception('Some error'); $kernel = $this->createMock(KernelInterface::class); $request = $this->createMock(Request::class); $event = new ExceptionEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $exception); $this->serializer->expects($this->never())->method('serialize'); $this->validationExceptionListener->onKernelException($event); $this->assertNull($event->getResponse()); } public function testItThrowsAnExceptionWhenSerializerIsNotAvailable(): void { $validationExceptionListener = new ValidationExceptionListener(null); $violationList = new ConstraintViolationList(); $exception = new ValidationException($violationList); $kernel = $this->createMock(KernelInterface::class); $request = $this->createMock(Request::class); $event = new ExceptionEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $exception); $this->expectException(\LogicException::class); $this->expectExceptionMessage('The Symfony Serializer is not available. Try running "composer require symfony/serializer".'); $validationExceptionListener->onKernelException($event); } } ================================================ FILE: src/Component/tests/Symfony/Validator/Exception/ValidationExceptionTest.php ================================================ createMock(ConstraintViolationInterface::class); $secondViolation = $this->createMock(ConstraintViolationInterface::class); $firstViolation->method('getPropertyPath')->willReturn('name'); $firstViolation->method('getMessage')->willReturn('This value should not be blank.'); $secondViolation->method('getPropertyPath')->willReturn('email'); $secondViolation->method('getMessage')->willReturn('This value should not be blank.'); $exception = new ValidationException(new ConstraintViolationList([ $firstViolation, $secondViolation, ])); $this->assertSame("name: This value should not be blank.\nemail: This value should not be blank.", (string) $exception); } public function testItCanBeConstructedWithAMessage(): void { $exception = new ValidationException(new ConstraintViolationList([]), 'You should not pass!'); $this->assertSame('You should not pass!', $exception->getMessage()); } public function testItCanBeConstructedWithACode(): void { $exception = new ValidationException(new ConstraintViolationList([]), '', 42); $this->assertSame(42, $exception->getCode()); } public function testItCanBeConstructedWithAPreviousException(): void { $previous = new \Exception(); $exception = new ValidationException(new ConstraintViolationList([]), '', 0, $previous); $this->assertSame($previous, $exception->getPrevious()); } } ================================================ FILE: src/Component/tests/Symfony/Validator/State/ValidateProviderTest.php ================================================ decorated = $this->createMock(ProviderInterface::class); $this->validator = $this->createMock(ValidatorInterface::class); $this->validateProvider = new ValidateProvider($this->decorated, $this->validator); } public function testItIsInitializable(): void { $this->assertInstanceOf(ValidateProvider::class, $this->validateProvider); } public function testItReturnsDataWhenResponseIsReturned(): void { $operation = new Create(); $context = new Context(); $response = new Response(); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($response); $this->validator->expects($this->never())->method('validate'); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($response, $result); } public function testItReturnsDataWhenOperationIsNotCreateOrUpdate(): void { $operation = new Index(); $context = new Context(); $data = new \stdClass(); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->validator->expects($this->never())->method('validate'); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItReturnsDataWhenValidationIsDisabled(): void { $operation = new Create(validate: false); $context = new Context(); $data = new \stdClass(); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->validator->expects($this->never())->method('validate'); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItValidatesDataForNonHtmlFormatWithoutViolations(): void { $operation = new Create(); $data = new \stdClass(); $request = new Request(); $request->setRequestFormat('json'); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->validator ->expects($this->once()) ->method('validate') ->with($data, null, null) ->willReturn(new ConstraintViolationList()); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItThrowsValidationExceptionForNonHtmlFormatWithViolations(): void { $operation = new Create(); $data = new \stdClass(); $request = new Request(); $request->setRequestFormat('json'); $context = new Context(new RequestOption($request)); $violation = new ConstraintViolation( 'This value should not be blank.', null, [], $data, 'name', null, ); $violations = new ConstraintViolationList([$violation]); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->validator ->expects($this->once()) ->method('validate') ->with($data, null, null) ->willReturn($violations); $this->expectException(ValidationException::class); $this->validateProvider->provide($operation, $context); } public function testItValidatesDataWithValidationGroups(): void { $operation = new Create(validationContext: ['groups' => ['Default', 'create']]); $data = new \stdClass(); $request = new Request(); $request->setRequestFormat('json'); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->validator ->expects($this->once()) ->method('validate') ->with($data, null, ['Default', 'create']) ->willReturn(new ConstraintViolationList()); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItReturnsDataWhenNoFormExistsForHtmlFormat(): void { $operation = new Create(); $data = new \stdClass(); $request = new Request(); $request->setRequestFormat('html'); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->validator->expects($this->never())->method('validate'); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItValidatesDataWhenNoRequestExists(): void { $operation = new Create(); $context = new Context(); $data = new \stdClass(); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->validator ->expects($this->once()) ->method('validate') ->with($data, null, null) ->willReturn(new ConstraintViolationList()); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($data, $result); } public function testItHandlesValidFormForHtmlFormat(): void { $operation = new Create(); $formData = new \stdClass(); $request = Request::create('/test', 'POST'); $request->setRequestFormat('html'); $form = $this->createMock(FormInterface::class); $form->expects($this->once())->method('isSubmitted')->willReturn(true); $form->expects($this->once())->method('isValid')->willReturn(true); $form->expects($this->once())->method('getData')->willReturn($formData); $request->attributes->set('form', $form); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn(new \stdClass()); $this->validator->expects($this->never())->method('validate'); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($formData, $result); $this->assertTrue($request->attributes->get('is_valid')); } public function testItHandlesInvalidFormForHtmlFormat(): void { $operation = new Create(); $data = new \stdClass(); $request = Request::create('/test', 'POST'); $request->setRequestFormat('html'); $form = $this->createMock(FormInterface::class); $form->expects($this->once())->method('isSubmitted')->willReturn(true); $form->expects($this->once())->method('isValid')->willReturn(false); $form->expects($this->never())->method('getData'); $request->attributes->set('form', $form); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->validator->expects($this->never())->method('validate'); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($data, $result); $this->assertFalse($request->attributes->get('is_valid')); } public function testItHandlesSafeMethodForHtmlFormat(): void { $operation = new Create(); $data = new \stdClass(); $request = Request::create('/test', 'GET'); $request->setRequestFormat('html'); $form = $this->createMock(FormInterface::class); $form->expects($this->never())->method('isSubmitted'); $form->expects($this->never())->method('isValid'); $form->expects($this->never())->method('getData'); $request->attributes->set('form', $form); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->validator->expects($this->never())->method('validate'); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($data, $result); $this->assertFalse($request->attributes->get('is_valid')); } public function testItValidatesUpdateOperation(): void { $operation = new Update(); $data = new \stdClass(); $request = new Request(); $request->setRequestFormat('json'); $context = new Context(new RequestOption($request)); $this->decorated ->expects($this->once()) ->method('provide') ->with($operation, $context) ->willReturn($data); $this->validator ->expects($this->once()) ->method('validate') ->with($data, null, null) ->willReturn(new ConstraintViolationList()); $result = $this->validateProvider->provide($operation, $context); $this->assertSame($data, $result); } } ================================================ FILE: src/Component/tests/Symfony/Workflow/OperationStateMachineTest.php ================================================ markAsSkippedIfSymfonyWorkflowIsNotAvailable(); $this->registry = $this->createMock(Registry::class); $this->operationStateMachine = new OperationStateMachine($this->registry); } public function testItIsInitializable(): void { $this->assertInstanceOf(OperationStateMachine::class, $this->operationStateMachine); } public function testItReturnsIfTransitionIsPossible(): void { $data = new \stdClass(); $operation = new Create(stateMachineTransition: 'publish'); $workflow = $this->createMock(Workflow::class); $this->registry->method('get')->with($data, null)->willReturn($workflow); $workflow->method('can')->with($data, 'publish')->willReturn(true); $result = $this->operationStateMachine->can($data, $operation, new Context()); $this->assertTrue($result); } public function testItAppliesTransition(): void { $data = new \stdClass(); $operation = new Create(stateMachineTransition: 'publish'); $workflow = $this->createMock(Workflow::class); $marking = $this->createMock(Marking::class); $this->registry->method('get')->with($data, null)->willReturn($workflow); $workflow->expects($this->once())->method('apply')->with($data, 'publish')->willReturn($marking); $this->operationStateMachine->apply($data, $operation, new Context()); } public function testItThrowsAnExceptionWhenOperationHasNoDefinedTransition(): void { $data = new \stdClass(); $operation = new Create(name: 'app_dummy_create'); $workflow = $this->createMock(Workflow::class); $this->registry->method('get')->with($data, null)->willReturn($workflow); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('No State machine transition was found on operation "app_dummy_create".'); $this->operationStateMachine->can($data, $operation, new Context()); } public function testItThrowsAnExceptionWhenSymfonyWorkflowIsNotAvailable(): void { $operationStateMachine = new OperationStateMachine(null); $data = new \stdClass(); $operation = new Create(stateMachineTransition: 'publish'); $this->expectException(\LogicException::class); $this->expectExceptionMessage('You can not use the "state-machine" if Symfony workflow is not available. Try running "composer require symfony/workflow".'); $operationStateMachine->can($data, $operation, new Context()); } public function testItThrowsAnExceptionWhenOperationDoesNotImplementAStateMachine(): void { $data = new \stdClass(); $operation = new Index(); $this->expectException(\LogicException::class); $this->expectExceptionMessage(sprintf('Expected an instance of %s. Got: %s', StateMachineAwareOperationInterface::class, Index::class)); $this->operationStateMachine->can($data, $operation, new Context()); } private function markAsSkippedIfSymfonyWorkflowIsNotAvailable(): void { if (!class_exists(Registry::class)) { $this->markTestSkipped('Symfony Workflow is not available.'); } } } ================================================ FILE: src/Component/tests/Translation/Provider/ImmutableTranslationLocaleProviderTest.php ================================================ localeProvider = new ImmutableTranslationLocaleProvider(['pl_PL', 'en_US'], 'pl_PL'); } public function testItImplementsTranslationLocaleProviderInterface(): void { $this->assertInstanceOf(TranslationLocaleProviderInterface::class, $this->localeProvider); } public function testItReturnsDefinedLocalesCodes(): void { $this->assertSame(['pl_PL', 'en_US'], $this->localeProvider->getDefinedLocalesCodes()); } public function testItReturnsTheDefaultLocaleCode(): void { $this->assertSame('pl_PL', $this->localeProvider->getDefaultLocaleCode()); } } ================================================ FILE: src/Component/tests/Translation/TranslatableEntityLocaleAssignerTest.php ================================================ translationLocaleProvider = $this->createMock(TranslationLocaleProviderInterface::class); $this->localeAssigner = new TranslatableEntityLocaleAssigner($this->translationLocaleProvider); } public function testItImplementsTranslatableEntityLocaleAssignerInterface(): void { $this->assertInstanceOf(TranslatableEntityLocaleAssignerInterface::class, $this->localeAssigner); } public function testItAssignsCurrentAndDefaultLocaleToGivenTranslatableEntity(): void { $translatableEntity = $this->createMock(TranslatableInterface::class); $this->translationLocaleProvider ->method('getDefaultLocaleCode') ->willReturn('en_US'); $translatableEntity->expects($this->once()) ->method('setCurrentLocale') ->with('en_US'); $translatableEntity->expects($this->once()) ->method('setFallbackLocale') ->with('en_US'); $this->localeAssigner->assignLocale($translatableEntity); } } ================================================ FILE: src/Component/tests/Twig/Context/Factory/ContextFactoryTest.php ================================================ locator = $this->createMock(ContainerInterface::class); $this->contextFactory = new ContextFactory($this->locator); } public function testItIsInitializable(): void { $this->assertInstanceOf(ContextFactory::class, $this->contextFactory); } public function testItCreatesTwigContextFromOperationTwigContextFactoryAsCallable(): void { $data = new \stdClass(); $twigContextFactory = [TwigContextFactoryCallable::class, 'create']; $operation = new Show(twigContextFactory: $twigContextFactory); $context = new Context(); $result = $this->contextFactory->create($data, $operation, $context); $this->assertSame(['foo' => 'bar'], $result); } public function testItCreatesTwigContextFromOperationTwigContextFactoryAsString(): void { $data = new \stdClass(); $twigContextFactory = $this->createMock(ContextFactoryInterface::class); $operation = new Show(twigContextFactory: $twigContextFactory::class); $context = new Context(); $this->locator->method('has')->with($twigContextFactory::class)->willReturn(true); $this->locator->method('get')->with($twigContextFactory::class)->willReturn($twigContextFactory); $twigContextFactory->method('create')->with($data, $operation, $context)->willReturn(['foo' => 'bar']); $result = $this->contextFactory->create($data, $operation, $context); $this->assertSame(['foo' => 'bar'], $result); } public function testItThrowsExceptionWhenTwigContextFactoryNotFoundOnLocator(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessage(sprintf('Twig context factory "%s" not found on operation "app_dummy_show"', ContextFactoryInterface::class)); $data = new \stdClass(); $operation = new Show(name: 'app_dummy_show', twigContextFactory: ContextFactoryInterface::class); $context = new Context(); $this->locator->method('has')->with(ContextFactoryInterface::class)->willReturn(false); $this->contextFactory->create($data, $operation, $context); } } final class TwigContextFactoryCallable { public static function create(mixed $data, Operation $operation, Context $context): array { return ['foo' => 'bar']; } } ================================================ FILE: src/Component/tests/Twig/Context/Factory/DefaultContextFactoryTest.php ================================================ defaultContextFactory = new DefaultContextFactory(); } public function testItIsInitializable(): void { $this->assertInstanceOf(DefaultContextFactory::class, $this->defaultContextFactory); } public function testItCreatesTwigContextForResource(): void { $data = new \stdClass(); $resourceMetadata = new ResourceMetadata(alias: 'app.dummy', name: 'dummy'); $operation = (new Show())->withResource($resourceMetadata); $context = new Context(); $result = $this->defaultContextFactory->create($data, $operation, $context); $this->assertSame([ 'operation' => $operation, 'resource_metadata' => $resourceMetadata, 'resource' => $data, 'dummy' => $data, ], $result); } public function testItCreatesTwigContextForResourceCollection(): void { $data = new \stdClass(); $resourceMetadata = new ResourceMetadata(alias: 'app.dummy', pluralName: 'dummies'); $operation = (new Index())->withResource($resourceMetadata); $context = new Context(); $result = $this->defaultContextFactory->create($data, $operation, $context); $this->assertSame([ 'operation' => $operation, 'resource_metadata' => $resourceMetadata, 'resources' => $data, 'dummies' => $data, ], $result); } } ================================================ FILE: src/Component/tests/Winzou/StateMachine/OperationStateMachineTest.php ================================================ markAsSkippedIfWinzouStateMachineIsNotAvailable(); $this->factory = $this->createMock(Factory::class); $this->operationStateMachine = new OperationStateMachine($this->factory); } public function testItReturnsIfTransitionIsPossible(): void { $data = new \stdClass(); $operation = new Create(stateMachineTransition: 'publish'); $stateMachine = $this->createMock(StateMachineInterface::class); $this->factory->method('get')->with($data, 'default')->willReturn($stateMachine); $stateMachine->method('can')->with('publish')->willReturn(true); $result = $this->operationStateMachine->can($data, $operation, new Context()); $this->assertTrue($result); } public function testItAppliesTransition(): void { $data = new \stdClass(); $operation = new Create(stateMachineTransition: 'publish'); $stateMachine = $this->createMock(StateMachineInterface::class); $this->factory->method('get')->with($data, 'default')->willReturn($stateMachine); $stateMachine->method('apply')->with('publish')->willReturn(true); $this->operationStateMachine->apply($data, $operation, new Context()); $this->assertTrue(true); } public function testItThrowsAnExceptionWhenOperationHasNoDefinedTransition(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('No State machine transition was found on operation "app_dummy_create".'); $data = new \stdClass(); $operation = new Create(name: 'app_dummy_create'); $stateMachine = $this->createMock(StateMachineInterface::class); $this->factory->method('get')->with($data, 'default')->willReturn($stateMachine); $this->operationStateMachine->can($data, $operation, new Context()); } public function testItThrowsAnExceptionWhenWinzouStateMachineIsNotAvailable(): void { $this->expectException(\LogicException::class); $this->expectExceptionMessage('You can not use the "state-machine" if Winzou State Machine is not available. Try running "composer require winzou/state-machine-bundle".'); $operationStateMachine = new OperationStateMachine(null); $data = new \stdClass(); $operation = new Create(stateMachineTransition: 'publish'); $operationStateMachine->can($data, $operation, new Context()); } public function testItThrowsAnExceptionWhenOperationDoesNotImplementAStateMachine(): void { $this->expectException(\LogicException::class); $this->expectExceptionMessage(sprintf('Expected an instance of %s. Got: %s', StateMachineAwareOperationInterface::class, Index::class)); $data = new \stdClass(); $operation = new Index(); $this->operationStateMachine->can($data, $operation, new Context()); } private function markAsSkippedIfWinzouStateMachineIsNotAvailable(): void { if (!class_exists(winzouStateMachineBundle::class)) { $this->markTestSkipped('Winzou State machine is not available.'); } } } ================================================ FILE: tests/ApiTestCase.php ================================================ client = self::createClient(); } protected function assertResponseMatchesPattern(string $pattern): void { $response = $this->client->getResponse(); $content = $response->getContent(); self::assertMatchesPattern($pattern, $content); } } ================================================ FILE: tests/Application/.gitignore ================================================ ###> symfony/framework-bundle ### /.env.*.local /.env.local /.env.local.php /public/bundles /var/ /vendor/ ###< symfony/framework-bundle ### config/db.sql ================================================ FILE: tests/Application/bin/console ================================================ #!/usr/bin/env php getParameterOption(['--env', '-e'], null, true)) { putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); } if ($input->hasParameterOption('--no-debug', true)) { putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); } require dirname(__DIR__) . '/config/bootstrap.php'; if ($_SERVER['APP_DEBUG']) { umask(0000); if (class_exists(Debug::class)) { Debug::enable(); } } $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); $application = new Application($kernel); $application->run($input); ================================================ FILE: tests/Application/composer.json ================================================ { "name": "example/test-application" } ================================================ FILE: tests/Application/config/bootstrap.php ================================================ =1.2) if (is_array($env = @include dirname(__DIR__) . '/.env.local.php')) { $_SERVER += $env; $_ENV += $env; } elseif (!class_exists(Dotenv::class)) { throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); } else { // load all the .env files (new Dotenv())->loadEnv(dirname(__DIR__) . '/.env'); } $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'test'; $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], \FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; ================================================ FILE: tests/Application/config/bundles.php ================================================ ['all' => true], SecurityBundle::class => ['all' => true], DoctrineBundle::class => ['all' => true], SyliusResourceBundle::class => ['all' => true], BabDevPagerfantaBundle::class => ['all' => true], TwigBundle::class => ['all' => true, 'test_without_twig' => false], SyliusGridBundle::class => ['all' => true, 'test_without_twig' => false], Zenstruck\Foundry\ZenstruckFoundryBundle::class => ['dev' => true, 'test' => true], ]; if (class_exists(BazingaHateoasBundle::class)) { $bundles[BazingaHateoasBundle::class] = ['all' => true, 'test_with_attributes' => false]; } if (class_exists(FOSRestBundle::class)) { $bundles[FOSRestBundle::class] = ['all' => true]; } if (class_exists(JMSSerializerBundle::class)) { $bundles[JMSSerializerBundle::class] = ['all' => true]; } if (class_exists(winzouStateMachineBundle::class)) { $bundles[winzouStateMachineBundle::class] = ['all' => true]; } return $bundles; ================================================ FILE: tests/Application/config/integration/fos_rest.yaml ================================================ fos_rest: view: formats: json: true empty_content: 204 format_listener: rules: - { path: '^/admin/*', priorities: ['html'], fallback_format: html } - { path: '^/science-books/*', priorities: ['html'], fallback_format: html } - { path: '^/', priorities: ['json'], fallback_format: json, prefer_extension: true } exception: enabled: false ================================================ FILE: tests/Application/config/integration/jms_serializer.yaml ================================================ jms_serializer: visitors: xml_serialization: format_output: '%kernel.debug%' ================================================ FILE: tests/Application/config/integration/symfony_workflow.yaml ================================================ framework: workflows: pull_request: type: 'state_machine' marking_store: type: 'method' property: 'currentPlace' supports: - App\Entity\PullRequest initial_marking: start places: - start - coding - test - review - merged - closed transitions: submit: from: start to: test update: from: [coding, test, review] to: test wait_for_review: from: test to: review request_change: from: review to: coding accept: from: review to: merged reject: from: review to: closed reopen: from: closed to: review blog_publishing: type: 'workflow' marking_store: type: 'method' property: 'currentPlace' supports: - App\Entity\BlogPost initial_marking: draft places: - draft - reviewed - rejected - published transitions: to_review: from: draft to: reviewed publish: from: reviewed to: published reject: from: reviewed to: rejected subscription: type: 'state_machine' marking_store: type: 'method' property: 'state' supports: - App\Subscription\Entity\Subscription initial_marking: new places: - new - accepted - rejected transitions: accept: from: new to: accepted reject: from: new to: rejected ================================================ FILE: tests/Application/config/integration/winzou_state_machine.yaml ================================================ winzou_state_machine: pull_request: class: App\Entity\PullRequest property_path: currentPlace graph: pull_request states: - start - coding - test - review - merged - closed transitions: submit: from: [ start ] to: test update: from: [ coding, test, review ] to: test wait_for_review: from: [ test ] to: review request_change: from: [ review ] to: coding accept: from: [ review ] to: merged reject: from: [ review ] to: closed reopen: from: [ closed ] to: review subscription: class: App\Subscription\Entity\Subscription property_path: state graph: subscription states: - new - accepted - rejected transitions: accept: from: [ new ] to: accepted reject: from: [ new ] to: rejected ================================================ FILE: tests/Application/config/packages/doctrine.yaml ================================================ doctrine: dbal: driver: "%database_driver%" path: "%database_path%" charset: UTF8 orm: naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware auto_mapping: true mappings: App: is_bundle: false type: attribute dir: '%kernel.project_dir%/src/Entity' prefix: 'App\Entity' alias: App BoardGameBlog: is_bundle: false type: attribute dir: '%kernel.project_dir%/src/BoardGameBlog/Domain' prefix: 'App\BoardGameBlog\Domain' Conference: is_bundle: false type: attribute dir: '%kernel.project_dir%/src/Conference/Entity' prefix: 'App\Conference\Entity' Subscription: is_bundle: false type: attribute dir: '%kernel.project_dir%/src/Subscription/Entity' prefix: 'App\Subscription\Entity' ================================================ FILE: tests/Application/config/packages/framework.yaml ================================================ framework: assets: false translator: default_path: '%kernel.project_dir%/translations' fallbacks: ["en_US"] secret: "%secret%" form: ~ csrf_protection: true default_locale: "en_US" session: handler_id: ~ storage_factory_id: session.storage.factory.mock_file http_method_override: true test: ~ serializer: enabled: true ================================================ FILE: tests/Application/config/packages/messenger.yaml ================================================ framework: messenger: default_bus: command.bus buses: command.bus: ~ query.bus: ~ transports: sync: 'sync://' routing: 'App\Shared\Application\Query\QueryInterface': 'sync' 'App\Shared\Application\Command\CommandInterface': 'sync' ================================================ FILE: tests/Application/config/packages/routing.yaml ================================================ framework: router: utf8: true ================================================ FILE: tests/Application/config/packages/security.yaml ================================================ security: # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: users_in_memory: { memory: null } firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true provider: users_in_memory # activate different ways to authenticate # https://symfony.com/doc/current/security.html#the-firewall # https://symfony.com/doc/current/security/impersonating_user.html # switch_user: true # Easy way to control access for large sections of your site # Note: Only the *first* access control that matches will be used access_control: # - { path: ^/admin, roles: ROLE_ADMIN } # - { path: ^/profile, roles: ROLE_USER } when@test: security: password_hashers: # By default, password hashers are resource intensive and take time. This is # important to generate secure password hashes. In tests however, secure hashes # are not important, waste resources and increase test times. The following # reduces the work factor to the lowest possible values. Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: algorithm: auto cost: 4 # Lowest possible value for bcrypt time_cost: 3 # Lowest possible value for argon memory_cost: 10 # Lowest possible value for argon ================================================ FILE: tests/Application/config/packages/test/grids.yaml ================================================ sylius_grid: grids: science_book_grid: driver: options: class: '%app.model.science_book.class%' sorting: title: asc fields: title: type: string sortable: ~ authorFirstName: type: string sortable: author.firstName authorLastName: type: string sortable: author.firstName filters: search: type: string options: fields: [title, author.firstName, author.lastName] ================================================ FILE: tests/Application/config/packages/test/sylius_grid.yaml ================================================ sylius_grid: templates: action: apply_transition: 'grid/action/apply_transition.html.twig' delete: 'grid/action/delete.html.twig' show: 'grid/action/show.html.twig' update: 'grid/action/update.html.twig' bulk_action: apply_transition: 'grid/bulk_action/apply_transition.html.twig' delete: 'grid/bulk_action/delete.html.twig' ================================================ FILE: tests/Application/config/packages/test/twig.yaml ================================================ twig: debug: "%kernel.debug%" strict_variables: "%kernel.debug%" paths: ['%kernel.project_dir%/templates'] ================================================ FILE: tests/Application/config/packages/test_with_attributes/sylius_resource.yaml ================================================ sylius_resource: mapping: paths: ['%kernel.project_dir%/src/Entity'] ================================================ FILE: tests/Application/config/packages/test_without_fosrest/grids.yaml ================================================ imports: - { resource: '../test/grids.yaml' } ================================================ FILE: tests/Application/config/packages/test_without_fosrest/sylius_grid.yaml ================================================ imports: - { resource: '../test/sylius_grid.yaml' } ================================================ FILE: tests/Application/config/packages/test_without_fosrest/twig.yaml ================================================ imports: - { resource: '../test/twig.yaml' } ================================================ FILE: tests/Application/config/packages/test_without_hateoas/fos_rest.yaml ================================================ imports: - { resource: '../test/fos_rest.yaml' } ================================================ FILE: tests/Application/config/packages/test_without_hateoas/grids.yaml ================================================ imports: - { resource: '../test/grids.yaml' } ================================================ FILE: tests/Application/config/packages/test_without_hateoas/jms_serializer.yaml ================================================ imports: - { resource: '../test/jms_serializer.yaml' } ================================================ FILE: tests/Application/config/packages/test_without_hateoas/sylius_grid.yaml ================================================ imports: - { resource: '../test/sylius_grid.yaml' } ================================================ FILE: tests/Application/config/packages/test_without_hateoas/twig.yaml ================================================ imports: - { resource: '../test/twig.yaml' } ================================================ FILE: tests/Application/config/routes/sylius_resource.yaml ================================================ sylius_crud_routes: resource: 'sylius.routing.loader.crud_routes_attributes' type: service sylius_resource_routes: resource: 'sylius.symfony.routing.loader.resource' type: service ================================================ FILE: tests/Application/config/routes.yaml ================================================ app_blog_post: resource: | alias: app.blog_post type: sylius.resource_api app_blog_post_apply_transition: path: /blog-posts/{id}/{transition} methods: [PUT] defaults: _controller: app.controller.blog_post::applyStateMachineTransitionAction _sylius: csrf_protection: false state_machine: graph: blog_publishing transition: $transition app_book: resource: | alias: app.book type: sylius.resource_api app_gedmo: resource: | alias: app.gedmo type: sylius.resource_api app_pull_request: resource: | alias: app.pull_request type: sylius.resource_api app_pull_request_apply_transition: path: /pull-requests/{id}/{transition} methods: [PUT] defaults: _controller: app.controller.pull_request::applyStateMachineTransitionAction _sylius: csrf_protection: false state_machine: graph: pull_request transition: $transition app_book_sortable_index: path: /sortable-books/ methods: [GET] defaults: _controller: App\Controller\BookController::indexAction _sylius: sortable: true app_book_filterable_index: path: /filterable-books/ methods: [GET] defaults: _controller: app.controller.book::indexAction _sylius: filterable: true app_versioned_book: resource: "versioned_routing.yaml" prefix: /v{version} app_create_book_with_custom_factory: path: /create-custom-book methods: [POST] defaults: _controller: app.controller.book::createAction _sylius: factory: method: ["expr:service('test.custom_book_factory')", "createCustom"] app_index_books_with_custom_repository: path: /find-custom-books methods: [GET] defaults: _controller: app.controller.book::indexAction _sylius: repository: method: ["expr:service('test.custom_book_repository')", "findCustomBooks"] app_show_book_with_custom_repository: path: /find-custom-book methods: [GET] defaults: _controller: app.controller.book::showAction _sylius: repository: method: ["expr:service('test.custom_book_repository')", "findCustomBook"] arguments: ["J.R.R. Tolkien"] app_science_book: resource: | alias: app.science_book templates: 'ScienceBook' grid: science_book_grid type: sylius.resource ================================================ FILE: tests/Application/config/services/integration/gedmo.yaml ================================================ services: _defaults: public: false gedmo_doctrine_extensions.mapping.driver.attribute: class: Gedmo\Mapping\Driver\AttributeReader gedmo.listener.sortable: class: Gedmo\Sortable\SortableListener tags: - { name: doctrine.event_listener, event: 'onFlush' } - { name: doctrine.event_listener, event: 'loadClassMetadata' } - { name: doctrine.event_listener, event: 'prePersist' } - { name: doctrine.event_listener, event: 'postPersist' } - { name: doctrine.event_listener, event: 'preUpdate' } - { name: doctrine.event_listener, event: 'postRemove' } - { name: doctrine.event_listener, event: 'postFlush' } calls: - [ setAnnotationReader, [ "@gedmo_doctrine_extensions.mapping.driver.attribute" ] ] ================================================ FILE: tests/Application/config/services.php ================================================ autowire(FirstAutowiredService::class)->setPublic(true); $container->autowire(SecondAutowiredService::class)->setPublic(true); $container->autowire(NoInterfaceAutowiredService::class)->setPublic(true); } else { $container->setDefinition(FirstAutowiredService::class, (new Definition(FirstAutowiredService::class, [ new Reference('app.factory.book'), new Reference('app.repository.book'), new Reference('app.manager.book'), ]))->setPublic(true)); $container->setDefinition(SecondAutowiredService::class, (new Definition(SecondAutowiredService::class, [ new Reference('app.factory.book'), new Reference('app.repository.book'), new Reference('app.manager.book'), ]))->setPublic(true)); $container->setDefinition(NoInterfaceAutowiredService::class, (new Definition(NoInterfaceAutowiredService::class, [ new Reference('app.factory.book'), new Reference('app.repository.book'), new Reference('app.factory.comic_book'), new Reference('app.repository.comic_book'), ]))->setPublic(true)); } ================================================ FILE: tests/Application/config/services.yaml ================================================ imports: - { resource: "sylius/resources.yaml" } parameters: database_driver: pdo_sqlite database_path: "%kernel.project_dir%/config/db.sql" secret: "Three can keep a secret, if two of them are dead." services: # Gets rid of "[error] Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException" # errors in PHPUnit output when testing 404 errors. Psr\Log\NullLogger: ~ logger: '@Psr\Log\NullLogger' # Default configuration for services in *this* file _defaults: # Automatically injects dependencies in your services autowire: true # Automatically registers your services as commands, event subscribers, etc. autoconfigure: true # Allows optimizing the container by removing unused services; this also means # fetching services directly from the container via $container->get() won't work public: false # Controllers are imported separately to make sure services can be injected # as action arguments even if you don't extend any base controller class App\Controller\: resource: '../src/Controller' tags: ['controller.service_arguments'] test.translation_locale_provider: class: Sylius\Component\Resource\Translation\Provider\ImmutableTranslationLocaleProvider arguments: - ["pl_PL", "en_US", "de_DE"] - "en_US" test.custom_book_factory: class: App\Factory\CustomBookFactory public: true arguments: - "%app.model.book.class%" - "@test.translation_locale_provider" test.custom_book_repository: class: App\Repository\CustomBookRepository public: true arguments: - "@app.repository.book" app.form.type.book: class: App\Form\Type\BookType arguments: - '%app.model.book.class%' tags: ['form.type'] app.form.type.science_book: class: App\Form\Type\ScienceBookType arguments: - '%app.model.science_book.class%' - ['sylius'] tags: [ 'form.type' ] app.form.type.book_translation: class: App\Form\Type\BookTranslationType arguments: - '%app.model.book_translation.class%' tags: ['form.type'] App\Repository\ComicBookRepository: null App\Repository\LegacyBookRepository: null App\Shared\: resource: '../src/Shared' app.service.legacy_autowired_repository: class: App\Service\LegacyAutowiredRepositoryService autowire: true app.service.legacy_autowired_factory: class: App\Service\LegacyAutowiredFactoryService autowire: true app.service.legacy_autowired_translatable_factory: class: App\Service\LegacyAutowiredTranslatableFactoryService autowire: true # Grid-dependent namespaces are loaded differently depending on environment when@test: services: App\BoardGameBlog\: resource: '../src/BoardGameBlog' autowire: true autoconfigure: true App\Conference\: resource: '../src/Conference' autowire: true autoconfigure: true App\Subscription\: resource: '../src/Subscription' autowire: true autoconfigure: true App\Subscription\Factory\SubscriptionFactory: decorates: 'app.factory.subscription' ================================================ FILE: tests/Application/config/sylius/resources/imports/speaker.php ================================================ withRoutePrefix('/admin') ->withClass(Speaker::class) ->withSection('admin') ->withTemplatesDir('crud') ->withFormType(SpeakerType::class) ->withOperations(new Operations([ new Create(), new Update(), new Delete(), new Index(grid: SpeakerGrid::class), ])) ; ================================================ FILE: tests/Application/config/sylius/resources.yaml ================================================ sylius_resource: mapping: imports: - '%kernel.project_dir%/config/sylius/resources/imports' paths: - '%kernel.project_dir%/src/BoardGameBlog/Infrastructure/Sylius/Resource' - '%kernel.project_dir%/src/Subscription/Entity' translation: locale_provider: test.translation_locale_provider resources: app.blog_post: classes: model: App\Entity\BlogPost form: App\Form\Type\BlogPostType app.book: classes: model: App\Entity\Book factory: App\Factory\BookFactory controller: App\Controller\BookController repository: App\Repository\BookRepository form: App\Form\Type\BookType translation: classes: model: App\Entity\BookTranslation interface: App\Entity\BookTranslationInterface form: App\Form\Type\BookTranslationType app.comic_book: classes: model: App\Entity\ComicBook app.legacy_book: classes: model: App\Entity\LegacyBook factory: App\Factory\LegacyBookFactory translation: classes: model: App\Entity\LegacyBookTranslation app.science_book: classes: model: App\Entity\ScienceBook form: App\Form\Type\ScienceBookType app.gedmo: classes: model: App\Entity\GedmoExtendedExample app.pull_request: classes: model: App\Entity\PullRequest form: App\Form\Type\PullRequestType app.speaker: classes: model: App\Conference\Entity\Speaker app.zone: classes: model: App\Entity\Zone\Zone interface: App\Entity\Zone\ZoneInterface app.zone_member: classes: model: App\Entity\Zone\ZoneMember interface: App\Entity\Zone\ZoneMemberInterface ================================================ FILE: tests/Application/config/validator/validation.yaml ================================================ App\Entity\ScienceBook: properties: title: - NotBlank: groups: [sylius] author: - Valid: ~ App\Entity\Author: properties: firstName: - NotBlank: groups: [sylius] lastName: - NotBlank: groups: [sylius] ================================================ FILE: tests/Application/config/versioned_routing.yaml ================================================ app_comic_book: resource: | alias: app.comic_book serialization_version: $version type: sylius.resource_api ================================================ FILE: tests/Application/public/index.php ================================================ getParameterOption(['--env', '-e'], null, true)) { putenv('APP_ENV=' . $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); } if ($input->hasParameterOption('--no-debug', true)) { putenv('APP_DEBUG=' . $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); } require dirname(__DIR__) . '/config/bootstrap.php'; if ($_SERVER['APP_DEBUG']) { umask(0000); if (class_exists(Debug::class)) { Debug::enable(); } } $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send(); $kernel->terminate($request, $response); ================================================ FILE: tests/Application/src/BoardGameBlog/Application/Command/CreateBoardGameCommand.php ================================================ name, ); $this->boardGameRepository->save($boardGame); return $boardGame; } } ================================================ FILE: tests/Application/src/BoardGameBlog/Application/Command/DeleteBoardGameCommand.php ================================================ boardGameRepository->ofId($command->id)) { return; } $this->boardGameRepository->remove($boardGame); } } ================================================ FILE: tests/Application/src/BoardGameBlog/Application/Command/UpdateBoardGameCommand.php ================================================ boardGameRepository->ofId($command->id); if (null === $boardGame) { throw new MissingBoardGameException($command->id); } $boardGame->update( name: $command->name, ); $this->boardGameRepository->save($boardGame); return $boardGame; } } ================================================ FILE: tests/Application/src/BoardGameBlog/Application/Query/FindBoardGameQuery.php ================================================ repository->ofId($query->id); } } ================================================ FILE: tests/Application/src/BoardGameBlog/Domain/Exception/MissingBoardGameException.php ================================================ id = new BoardGameId(); } public function update( ?BoardGameName $name = null, ): void { $this->name = $name ?? $this->name; } public function id(): BoardGameId { return $this->id; } public function name(): BoardGameName { return $this->name; } } ================================================ FILE: tests/Application/src/BoardGameBlog/Domain/Repository/BoardGameRepositoryInterface.php ================================================ */ interface BoardGameRepositoryInterface extends RepositoryInterface { public function save(BoardGame $boardGame): void; public function remove(BoardGame $boardGame): void; public function ofId(BoardGameId $id): ?BoardGame; } ================================================ FILE: tests/Application/src/BoardGameBlog/Domain/ValueObject/BoardGameId.php ================================================ value = $value; } public function __toString(): string { return $this->value; } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Doctrine/DoctrineBoardGameRepository.php ================================================ getEntityManager()->persist($boardGame); $this->getEntityManager()->flush(); $this->getEntityManager()->refresh($boardGame); } public function remove(BoardGame $boardGame): void { $this->getEntityManager()->remove($boardGame); $this->getEntityManager()->flush(); } public function ofId(BoardGameId $id): ?BoardGame { return $this->find($id->value); } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Foundry/Factory/BoardGameFactory.php ================================================ */ final class BoardGameFactory extends PersistentObjectFactory { public static function class(): string { return BoardGame::class; } public function withName(BoardGameName $name): self { return $this->with(['name' => $name]); } protected function defaults(): array { return [ 'name' => new BoardGameName(ucfirst(self::faker()->words(2, true))), ]; } protected function initialize(): static { return parent::instantiateWith(function (array $attributes): BoardGame { return new BoardGame(...$attributes); }); } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Sylius/Grid/BoardGameGrid.php ================================================ orderBy('name', 'asc') ->addField( StringField::create('name') ->setPath('name.value') ->setLabel('Name') ->setSortable(true, 'name.value'), ) ->addActionGroup( MainActionGroup::create( CreateAction::create(), ), ) ->addActionGroup( ItemActionGroup::create( ShowAction::create(), UpdateAction::create(), DeleteAction::create(), ), ) ; } public function getResourceClass(): string { return BoardGame::class; } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Sylius/Resource/BoardGameResource.php ================================================ id()->value, $boardGame->name()->value, ); } public function getId(): ?string { return $this->id?->__toString(); } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Sylius/Resource/Mutator/BoardGameTemplatesDirMutator.php ================================================ withTemplatesDir('crud'); } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Sylius/Resource/Mutator/CreateBoardGameProcessorMutator.php ================================================ withProcessor(CreateBoardGameProcessor::class); } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/CreateBoardGameProcessor.php ================================================ name); $command = new CreateBoardGameCommand( new BoardGameName($data->name), ); /** @var BoardGame $model */ $model = $this->commandBus->dispatch($command); return BoardGameResource::fromModel($model); } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/DeleteBoardGameProcessor.php ================================================ commandBus->dispatch(new DeleteBoardGameCommand(new BoardGameId($data->id))); return null; } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/UpdateBoardGameProcessor.php ================================================ id), null !== $data->name ? new BoardGameName($data->name) : null, ); $model = $this->commandBus->dispatch($command); return BoardGameResource::fromModel($model); } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Provider/BoardGameCollectionProvider.php ================================================ requestGridProvider->provide($operation, $context); } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Provider/BoardGameItemProvider.php ================================================ get(RequestOption::class)?->request(); Assert::notNull($request); $id = (string) $request->attributes->get('id'); $model = $this->queryBus->ask(new FindBoardGameQuery(new BoardGameId(Uuid::fromString($id)))); return null !== $model ? BoardGameResource::fromModel($model) : null; } } ================================================ FILE: tests/Application/src/BoardGameBlog/Infrastructure/Symfony/Form/Type/BoardGameType.php ================================================ add('name') ; } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'data_class' => BoardGameResource::class, ]); } } ================================================ FILE: tests/Application/src/Conference/Entity/Speaker.php ================================================ id; } public function getFullName(): ?string { return $this->firstName . ' ' . $this->lastName; } } ================================================ FILE: tests/Application/src/Conference/Factory/SpeakerFactory.php ================================================ */ final class SpeakerFactory extends PersistentObjectFactory { public static function class(): string { return Speaker::class; } public function withFirstName(string $firstName): self { return $this->with(['firstName' => $firstName]); } public function withLastName(string $lastName): self { return $this->with(['lastName' => $lastName]); } protected function defaults(): array { return [ 'firstName' => self::faker()->firstName(), 'lastName' => self::faker()->lastName(), ]; } } ================================================ FILE: tests/Application/src/Conference/Form/SpeakerType.php ================================================ add('firstName') ->add('lastName') ; } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'data_class' => Speaker::class, ]); } } ================================================ FILE: tests/Application/src/Conference/Grid/SpeakerGrid.php ================================================ orderBy('fullName', 'asc') ->setLimits([10, 25, 50]) ->addField( StringField::create('fullName') ->setLabel('Name') ->setSortable(true, 'firstName'), ) ->addActionGroup( MainActionGroup::create( CreateAction::create(), ), ) ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), DeleteAction::create(), ), ) ; } public function getResourceClass(): string { return Speaker::class; } } ================================================ FILE: tests/Application/src/Controller/BookController.php ================================================ firstName; } public function setFirstName(?string $firstName): void { $this->firstName = $firstName; } public function getLastName(): ?string { return $this->lastName; } public function setLastName(?string $lastName): void { $this->lastName = $lastName; } } ================================================ FILE: tests/Application/src/Entity/BlogPost.php ================================================ 1]; public function getId(): ?int { return $this->id; } public function getCurrentPlace(): array { return $this->currentPlace; } public function setCurrentPlace(array $currentPlace) { $this->currentPlace = $currentPlace; } } ================================================ FILE: tests/Application/src/Entity/Book.php ================================================ id; } #[Serializer\VirtualProperty] #[Serializer\SerializedName(name: 'title')] public function getTitle(): ?string { return $this->getTranslation()->getTitle(); } public function setTitle(?string $title): void { $this->getTranslation()->setTitle($title); } public function getAuthor(): ?string { return $this->author; } public function setAuthor(?string $author): void { $this->author = $author; } protected function createTranslation(): BookTranslation { return new BookTranslation(); } } ================================================ FILE: tests/Application/src/Entity/BookTranslation.php ================================================ id; } public function getTitle(): ?string { return $this->title; } public function setTitle(?string $title): void { $this->title = $title; } } ================================================ FILE: tests/Application/src/Entity/BookTranslationInterface.php ================================================ id; } public function getTitle(): ?string { return $this->title; } public function setTitle(?string $title): void { $this->title = $title; } #[Serializer\VirtualProperty] #[Serializer\Since(version: '1.1')] public function getAuthorFirstName(): ?string { return $this->author?->getFirstName(); } #[Serializer\VirtualProperty] #[Serializer\Since(version: '1.1')] public function getAuthorLastName(): ?string { return $this->author?->getLastName(); } public function getAuthor(): ?Author { return $this->author; } public function setAuthor(?Author $author): void { $this->author = $author; } } ================================================ FILE: tests/Application/src/Entity/CrudRoutes/BookWithAlias.php ================================================ '$libraryId'], )] class BookWithCriteria extends Book { } ================================================ FILE: tests/Application/src/Entity/CrudRoutes/BookWithExcept.php ================================================ [ 'subheader' => 'app.ui.manage_your_books', ], 'index' => [ 'icon' => 'book', ], ], )] class BookWithVars extends Book { } ================================================ FILE: tests/Application/src/Entity/GedmoBaseExample.php ================================================ id; } public function getPosition(): ?int { return $this->position; } public function setPosition(?int $position): void { $this->position = $position; } } ================================================ FILE: tests/Application/src/Entity/GedmoExtendedExample.php ================================================ extra; } public function setExtra(?string $extra): void { $this->extra = $extra; } } ================================================ FILE: tests/Application/src/Entity/LegacyBook.php ================================================ id; } protected function createTranslation(): LegacyBookTranslation { return new LegacyBookTranslation(); } } ================================================ FILE: tests/Application/src/Entity/LegacyBookTranslation.php ================================================ id; } public function getTitle(): ?string { return $this->title; } public function setTitle(?string $title): void { $this->title = $title; } } ================================================ FILE: tests/Application/src/Entity/PullRequest.php ================================================ id; } public function getCurrentPlace(): string { return $this->currentPlace; } public function setCurrentPlace(string $currentPlace): void { $this->currentPlace = $currentPlace; } } ================================================ FILE: tests/Application/src/Entity/Route/PublishBook.php ================================================ 'app_book', 'transition' => 'publish'], )] class PublishBook extends Book { } ================================================ FILE: tests/Application/src/Entity/Route/RegisterUserWithForm.php ================================================ RegisterType::class, 'options' => [ 'validation_groups' => ['sylius', 'my_custom_group'], ], ], )] final class RegisterUserWithFormOptions extends User { } ================================================ FILE: tests/Application/src/Entity/Route/ShowBook.php ================================================ '$libraryId'], )] class ShowBookWithCriteria extends Book { } ================================================ FILE: tests/Application/src/Entity/Route/ShowBookWithCsrfProtection.php ================================================ true], )] class ShowBookWithOptions extends Book { } ================================================ FILE: tests/Application/src/Entity/Route/ShowBookWithPermission.php ================================================ 'findOneNewestByAuthor', 'arguments' => '[$author]', ], )] class ShowBookWithRepository extends Book { } ================================================ FILE: tests/Application/src/Entity/Route/ShowBookWithRequirements.php ================================================ '\d+'], )] class ShowBookWithRequirements extends Book { } ================================================ FILE: tests/Application/src/Entity/Route/ShowBookWithSchemes.php ================================================ 'bar'], )] class ShowBookWithVars extends Book { } ================================================ FILE: tests/Application/src/Entity/Route/UpdateBookWithCustomEventName.php ================================================ 'app_genre_show', 'parameters' => ['id' => '$genreId'], ], )] final class UpdateBookWithRedirectOptions extends Book { } ================================================ FILE: tests/Application/src/Entity/Route/UpdateBookWithReturnContent.php ================================================ id; } public function getTitle(): ?string { return $this->title; } public function setTitle(?string $title): void { $this->title = $title; } public function getAuthorFirstName(): ?string { return $this->author?->getFirstName(); } public function getAuthorLastName(): ?string { return $this->author?->getLastName(); } public function getAuthor(): ?Author { return $this->author; } public function setAuthor(?Author $author): void { $this->author = $author; } } ================================================ FILE: tests/Application/src/Entity/User.php ================================================ id; } public function getUsername(): ?string { return $this->username; } public function setUsername(?string $username): void { $this->username = $username; } public function getPassword(): ?string { return $this->password; } public function setPassword(?string $password): void { $this->password = $password; } } ================================================ FILE: tests/Application/src/Entity/Zone/Zone.php ================================================ id; } } ================================================ FILE: tests/Application/src/Entity/Zone/ZoneInterface.php ================================================ id; } } ================================================ FILE: tests/Application/src/Entity/Zone/ZoneMemberInterface.php ================================================ factory = $factory; $this->localeProvider = $localeProvider; } public function createNew() { $book = $this->factory->createNew(); if (!$book instanceof TranslatableInterface) { throw new UnexpectedTypeException($book, TranslatableInterface::class); } $book->setCurrentLocale($this->localeProvider->getDefaultLocaleCode()); $book->setFallbackLocale($this->localeProvider->getDefaultLocaleCode()); return $book; } } ================================================ FILE: tests/Application/src/Factory/BookFactoryInterface.php ================================================ className = $className; $this->localeProvider = $localeProvider; } public function createCustom(): Book { /** @var Book $book */ $book = new $this->className(); $book->setCurrentLocale($this->localeProvider->getDefaultLocaleCode()); $book->setFallbackLocale($this->localeProvider->getDefaultLocaleCode()); return $book; } } ================================================ FILE: tests/Application/src/Factory/LegacyBookFactory.php ================================================ factory = $factory; $this->localeProvider = $localeProvider; } public function createNew() { $book = $this->factory->createNew(); if (!$book instanceof TranslatableInterface) { throw new UnexpectedTypeException($book, TranslatableInterface::class); } $book->setCurrentLocale($this->localeProvider->getDefaultLocaleCode()); $book->setFallbackLocale($this->localeProvider->getDefaultLocaleCode()); return $book; } } ================================================ FILE: tests/Application/src/Form/Type/AuthorType.php ================================================ add('firstName', TextType::class) ->add('lastName', TextType::class) ; } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefault('data_class', Author::class); $resolver->setDefault('validation_groups', ['sylius']); } } ================================================ FILE: tests/Application/src/Form/Type/BlogPostType.php ================================================ setDefaults([ 'data_class' => BlogPost::class, ]) ; } public function getBlockPrefix(): string { return 'app_blog_post'; } } ================================================ FILE: tests/Application/src/Form/Type/BookTranslationType.php ================================================ add('title', TextType::class); } public function getBlockPrefix(): string { return 'app_book_translation'; } } ================================================ FILE: tests/Application/src/Form/Type/BookType.php ================================================ add('translations', ResourceTranslationsType::class, [ 'entry_type' => BookTranslationType::class, 'label' => 'title', ]) ->add('author', TextType::class) ; } public function getBlockPrefix(): string { return 'app_book'; } } ================================================ FILE: tests/Application/src/Form/Type/PullRequestType.php ================================================ setDefaults([ 'data_class' => PullRequest::class, ]) ; } public function getBlockPrefix(): string { return 'app_pull_request'; } } ================================================ FILE: tests/Application/src/Form/Type/RegisterType.php ================================================ add('title', TextType::class) ->add('author', AuthorType::class) ; } } ================================================ FILE: tests/Application/src/Foundry/Factory/AuthorFactory.php ================================================ */ final class AuthorFactory extends ObjectFactory { public static function class(): string { return Author::class; } public function withFirstName(string $firstName): self { return $this->with(['firstName' => $firstName]); } public function withLastName(string $lastName): self { return $this->with(['lastName' => $lastName]); } protected function defaults(): array { return [ 'firstName' => self::faker()->firstName(), 'lastName' => self::faker()->lastName(), ]; } } ================================================ FILE: tests/Application/src/Foundry/Factory/BlogPostFactory.php ================================================ */ final class BlogPostFactory extends PersistentObjectFactory { public static function class(): string { return BlogPost::class; } public function onDraft(): self { return $this->with(['currentPlace' => ['draft' => 1]]); } public function reviewed(): self { return $this->with(['currentPlace' => ['reviewed' => 1]]); } protected function defaults(): array { return []; } } ================================================ FILE: tests/Application/src/Foundry/Factory/BookFactory.php ================================================ */ final class BookFactory extends PersistentObjectFactory { public static function class(): string { return Book::class; } public function withTranslations(array $translations): self { return $this->with(['translations' => $translations]); } public function withTitle(string $title): self { return $this->with(['title' => $title]); } public function withAuthor(string $author): self { return $this->with(['author' => $author]); } protected function defaults(): array { return [ 'fallbackLocale' => 'en_US', 'currentLocale' => 'en_US', 'author' => self::faker()->firstName() . ' ' . self::faker()->lastName(), ]; } } ================================================ FILE: tests/Application/src/Foundry/Factory/BookTranslationFactory.php ================================================ */ final class BookTranslationFactory extends PersistentObjectFactory { public static function class(): string { return BookTranslation::class; } public function withLocale(string $locale): self { return $this->with(['locale' => $locale]); } public function withTitle(string $title): self { return $this->with(['title' => $title]); } protected function defaults(): array { return [ 'locale' => self::faker()->locale(), 'title' => ucfirst(self::faker()->words(2, true)), ]; } } ================================================ FILE: tests/Application/src/Foundry/Factory/ComicBookFactory.php ================================================ */ final class ComicBookFactory extends PersistentObjectFactory { public static function class(): string { return ComicBook::class; } public function withTitle(string $title): self { return $this->with(['title' => $title]); } /** * @param AuthorFactory|Proxy $author */ public function withAuthor(AuthorFactory|Proxy $author): self { return $this->with(['author' => $author]); } protected function defaults(): array { $author = LazyValue::memoize(fn () => AuthorFactory::createOne()); return [ 'title' => ucfirst(self::faker()->words(2, true)), 'author' => $author, ]; } } ================================================ FILE: tests/Application/src/Foundry/Factory/PullRequestFactory.php ================================================ */ final class PullRequestFactory extends PersistentObjectFactory { public static function class(): string { return PullRequest::class; } public function withCurrentPlace(string $currentPlace): self { return $this->with(['currentPlace' => $currentPlace]); } protected function defaults(): array { return [ 'currentPlace' => self::faker()->randomElement([ 'start', 'coding', 'test', 'review', 'merged', 'closed', ]), ]; } } ================================================ FILE: tests/Application/src/Foundry/Factory/ScienceBookFactory.php ================================================ */ final class ScienceBookFactory extends PersistentObjectFactory { public static function class(): string { return ScienceBook::class; } public function withTitle(string $title): self { return $this->with(['title' => $title]); } /** * @param AuthorFactory|Proxy $author */ public function withAuthor(AuthorFactory|Proxy $author): self { return $this->with(['author' => $author]); } protected function defaults(): array { return [ 'title' => ucfirst(self::faker()->words(2, true)), 'author' => lazy(fn () => AuthorFactory::new()), ]; } } ================================================ FILE: tests/Application/src/Foundry/Story/DefaultBooksStory.php ================================================ withTranslations([ BookTranslationFactory::new() ->withLocale('en_US') ->withTitle('Lord of The Rings'), BookTranslationFactory::new() ->withLocale('pl_PL') ->withTitle('Władca Pierścieni'), ]) ->withAuthor('J.R.R. Tolkien') ->create() ; BookFactory::new() ->withTitle('Game of Thrones') ->withAuthor('George R. R. Martin') ->create() ; } } ================================================ FILE: tests/Application/src/Foundry/Story/DefaultComicBooksStory.php ================================================ withTitle('Old Man Logan') ->withAuthor( AuthorFactory::new() ->withFirstName('Andrea') ->withLastName('Sorrentino'), ) ->create() ; ComicBookFactory::new() ->withTitle('Civil War II') ->withAuthor( AuthorFactory::new() ->withFirstName('Brian Michael') ->withLastName('Bendis'), ) ->create() ; } } ================================================ FILE: tests/Application/src/Foundry/Story/MoreBooksStory.php ================================================ 'Book ' . $number]; } }, ); } } ================================================ FILE: tests/Application/src/Kernel.php ================================================ registerForAutoconfiguration(QueryHandlerInterface::class) ->addTag('messenger.message_handler', ['bus' => 'query.bus']) ; $container->registerForAutoconfiguration(CommandHandlerInterface::class) ->addTag('messenger.message_handler', ['bus' => 'command.bus']) ; if (self::MAJOR_VERSION < 7) { $container->prependExtensionConfig('security', [ 'enable_authenticator_manager' => true, ]); } if (class_exists(Urlizer::class)) { $this->configureAppWithGedmoDoctrineExtensions($loader); } if (class_exists(FosRestBundle::class)) { $this->configureAppWithFosRestBundle($loader); } if (class_exists(JMSSerializerBundle::class)) { $this->configureAppWithJmsSerializerBundle($loader); } if (class_exists(winzouStateMachineBundle::class)) { $this->configureAppWithWinzouStateMachine($loader, $container); } if (class_exists(Registry::class)) { $this->configureAppWithSymfonyWorkflow($loader, $container); } } private function configureAppWithGedmoDoctrineExtensions(YamlFileLoader $loader): void { $loader->load('services/integration/gedmo.yaml'); } private function configureAppWithFosRestBundle(YamlFileLoader $loader): void { $loader->load('integration/fos_rest.yaml'); } private function configureAppWithJmsSerializerBundle(YamlFileLoader $loader): void { $loader->load('integration/jms_serializer.yaml'); } private function configureAppWithWinzouStateMachine(YamlFileLoader $loader, ContainerBuilder $container): void { $container->prependExtensionConfig('sylius_resource', [ 'settings' => [ 'state_machine_component' => 'winzou', ], ]); $loader->load('integration/winzou_state_machine.yaml'); } private function configureAppWithSymfonyWorkflow(YamlFileLoader $loader, ContainerBuilder $container): void { $container->prependExtensionConfig('sylius_resource', [ 'settings' => [ 'state_machine_component' => 'symfony', ], ]); $loader->load('integration/symfony_workflow.yaml'); } } ================================================ FILE: tests/Application/src/Repository/BookRepository.php ================================================ repository = $repository; } public function findCustomBook(string $author): ?Book { return $this->repository->findOneBy(['author' => $author]); } public function findCustomBooks(): iterable { return $this->repository->createPaginator(); } } ================================================ FILE: tests/Application/src/Repository/LegacyBookRepository.php ================================================ bookFactory = $bookFactory; $this->bookRepository = $bookRepository; $this->bookManager = $bookManager; } } ================================================ FILE: tests/Application/src/Service/LegacyAutowiredFactoryService.php ================================================ bookFactory = $bookFactory; $this->bookRepository = $bookRepository; $this->comicBookFactory = $comicBookFactory; $this->comicBookRepository = $comicBookRepository; } } ================================================ FILE: tests/Application/src/Service/SecondAutowiredService.php ================================================ bookFactory = $bookFactory; $this->bookRepository = $bookRepository; $this->bookManager = $bookManager; } } ================================================ FILE: tests/Application/src/Shared/Application/Command/CommandBusInterface.php ================================================ value = $value ?? Uuid::v4(); } public function __toString(): string { return (string) $this->value; } } ================================================ FILE: tests/Application/src/Shared/Infrastructure/Symfony/Messenger/MessengerCommandBus.php ================================================ messageBus = $commandBus; } public function dispatch(CommandInterface $command): mixed { try { return $this->handle($command); } catch (HandlerFailedException $e) { /** @var array{0: \Throwable} $exceptions */ $exceptions = $e->getWrappedExceptions(); throw reset($exceptions); } } } ================================================ FILE: tests/Application/src/Shared/Infrastructure/Symfony/Messenger/MessengerQueryBus.php ================================================ messageBus = $queryBus; } public function ask(QueryInterface $query): mixed { try { return $this->handle($query); } catch (HandlerFailedException $e) { /** @var array{0: \Throwable} $exceptions */ $exceptions = $e->getWrappedExceptions(); throw reset($exceptions); } } } ================================================ FILE: tests/Application/src/Shared/Infrastructure/Twig/SyliusStateMachineExtension.php ================================================ canTransition(...)), ]; } public function canTransition(object $subject, string $transitionName, ?string $name = null): bool { if (null !== $this->factory) { return $this->factory->get($subject, $name ?? 'default')->can($transitionName); } if (null !== $this->workflowRegistry) { return $this->workflowRegistry->get($subject, $name)->can($subject, $transitionName); } return false; } } ================================================ FILE: tests/Application/src/Subscription/Entity/Subscription.php ================================================ 'subscription:read'], denormalizationContext: ['groups' => 'subscription:write'], )] #[Api\GetCollection] #[Api\Post] #[Api\Put] #[Api\Delete] #[Api\Get] #[ORM\Entity(repositoryClass: SubscriptionRepository::class)] class Subscription implements ResourceInterface { #[ORM\Column(type: 'string')] #[Groups(['subscription:read'])] public string $state = 'new'; public function __construct( #[ORM\Id] #[ORM\Column(type: 'integer', unique: true)] #[ORM\GeneratedValue(strategy: 'AUTO')] public ?int $id = null, #[Assert\NotBlank] #[Assert\Email] #[ORM\Column(name: 'name', nullable: false)] #[Groups(['subscription:read', 'subscription:write'])] public ?string $email = null, ) { } public function getId(): ?int { return $this->id; } public function getState(): string { return $this->state; } public function setState(string $currentState): void { $this->state = $currentState; } } ================================================ FILE: tests/Application/src/Subscription/Entity/SubscriptionRepository.php ================================================ registry, Subscription::class); } } ================================================ FILE: tests/Application/src/Subscription/EventSubscriber/SmokeSubscriptionEventsSubscriber.php ================================================ 'smokeShowEvent', 'app.subscription.index' => 'smokeIndexEvent', 'app.subscription.pre_create' => 'smokePreEvent', 'app.subscription.post_create' => 'smokePostEvent', 'app.subscription.pre_update' => 'smokePreEvent', 'app.subscription.post_update' => 'smokePostEvent', 'app.subscription.pre_delete' => 'smokePreEvent', 'app.subscription.post_delete' => 'smokePostEvent', 'app.subscription.bulk_delete' => 'smokeBulkEvent', ]; } public function smokeShowEvent(SymfonyGenericEvent $event): void { } public function smokeIndexEvent(ResourceControllerEvent $event): void { } public function smokePreEvent(GenericEvent $event): void { } public function smokePostEvent(OperationEvent $event): void { } public function smokeBulkEvent(OperationEvent $event): void { } } ================================================ FILE: tests/Application/src/Subscription/Factory/SubscriptionFactory.php ================================================ add('email') ; } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'data_class' => Subscription::class, ]); } } ================================================ FILE: tests/Application/src/Subscription/Foundry/Factory/SubscriptionFactory.php ================================================ */ final class SubscriptionFactory extends PersistentObjectFactory { public static function class(): string { return Subscription::class; } public function withEmail(string $email): self { return $this->with(['email' => $email]); } public function accepted(): self { return $this->with(['state' => 'accepted']); } protected function defaults(): array { return [ 'email' => self::faker()->email(), ]; } } ================================================ FILE: tests/Application/src/Subscription/Foundry/Story/DefaultSubscriptionsStory.php ================================================ withEmail('marty.mcfly@bttf.com') ->create() ; SubscriptionFactory::new() ->withEmail('doc.brown@bttf.com') ->create() ; SubscriptionFactory::new() ->withEmail('biff.tannen@bttf.com') ->accepted() ->create() ; SubscriptionFactory::new() ->withEmail('lorraine.baines@bttf.com') ->create() ; SubscriptionFactory::new() ->withEmail('george.mcfly@bttf.com') ->create() ; SubscriptionFactory::new() ->withEmail('jennifer.parker@bttf.com') ->create() ; }); } } ================================================ FILE: tests/Application/src/Subscription/Grid/SubscriptionGrid.php ================================================ orderBy('email', 'asc') ->setLimits([5, 3, 10]) ->addField( StringField::create('email') ->setLabel('Email') ->setSortable(true), ) ->addActionGroup( MainActionGroup::create( CreateAction::create(), ), ) ->addActionGroup( ItemActionGroup::create( ShowAction::create(), UpdateAction::create(), DeleteAction::create(), Action::create('accept', 'apply_transition') ->setLabel('Accept') ->setOptions([ 'link' => [ 'route' => 'app_admin_subscription_accept', 'parameters' => [ 'id' => 'resource.id', ], ], 'class' => 'green', 'transition' => 'accept', 'graph' => 'subscription', ]), ), ) ->addActionGroup( BulkActionGroup::create( DeleteAction::create(), Action::create('accept', 'apply_transition') ->setLabel('Bulk accept') ->setOptions([ 'link' => [ 'route' => 'app_admin_subscription_bulk_accept', ], 'class' => 'green', 'transition' => 'accept', 'graph' => 'subscription', ]), ), ) ; } public function getResourceClass(): string { return Subscription::class; } } ================================================ FILE: tests/Application/src/Subscription/Twig/Context/Factory/ShowSubscriptionContextFactory.php ================================================ decorated->create($data, $operation, $context), [ 'foo' => 'bar', ]); } } ================================================ FILE: tests/Application/src/Tests/Controller/BlogPostApiTest.php ================================================ markAsSkippedIfFosRestBundleIsNotAvailable(); } #[Test] public function it_allows_creating_a_blog_post(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $this->markAsSkippedIfCurrentStateMachineIsNotTheSymfonyOne(); $this->client->request('POST', 'blog-posts', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "current_place": { "draft": 1 } } JSON ); } #[Test] public function it_allows_creating_a_blog_post_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $this->markAsSkippedIfCurrentStateMachineIsNotTheSymfonyOne(); $this->client->request('POST', '/blog-posts/', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "current_place": { "draft": 1 } } JSON ); } #[Test] public function it_allows_reviewing_a_blog_post(): void { $this->markAsSkippedIfCurrentStateMachineIsNotTheSymfonyOne(); $blogPost = BlogPostFactory::new() ->onDraft() ->create() ; $this->client->request('PUT', '/blog-posts/' . $blogPost->getId() . '/to_review', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "current_place": { "reviewed": 1 } } JSON ); } #[Test] public function it_allows_publishing_a_blog_post(): void { $this->markAsSkippedIfCurrentStateMachineIsNotTheSymfonyOne(); $blogPost = BlogPostFactory::new() ->reviewed() ->create() ; $this->client->request('PUT', '/blog-posts/' . $blogPost->getId() . '/publish', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "current_place": { "published": 1 } } JSON ); } #[Test] public function it_allows_rejecting_a_blog_post(): void { $this->markAsSkippedIfCurrentStateMachineIsNotTheSymfonyOne(); $blogPost = BlogPostFactory::new() ->reviewed() ->create() ; $this->client->request('PUT', '/blog-posts/' . $blogPost->getId() . '/reject', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "current_place": { "rejected": 1 } } JSON ); } #[Test] public function it_does_not_allow_to_publish_a_blog_post_with_draft_status(): void { $this->markAsSkippedIfCurrentStateMachineIsNotTheSymfonyOne(); $blogPost = BlogPostFactory::new() ->onDraft() ->create() ; $this->client->request('PUT', '/blog-posts/' . $blogPost->getId() . '/publish', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseStatusCodeSame(Response::HTTP_BAD_REQUEST); $this->assertResponseHeaderSame('content-type', 'application/json'); } #[Test] public function it_does_not_allow_to_reject_a_blog_post_with_draft_status(): void { $this->markAsSkippedIfCurrentStateMachineIsNotTheSymfonyOne(); $blogPost = BlogPostFactory::new() ->onDraft() ->create() ; $this->client->request('PUT', '/blog-posts/' . $blogPost->getId() . '/reject', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseStatusCodeSame(Response::HTTP_BAD_REQUEST); $this->assertResponseHeaderSame('content-type', 'application/json'); } private function markAsSkippedIfCurrentStateMachineIsNotTheSymfonyOne(): void { $container = self::getContainer(); $stateMachine = $container->getParameter('sylius.resource.settings')['state_machine_component']; if (ResourceBundleInterface::STATE_MACHINE_SYMFONY !== $stateMachine) { $this->markTestSkipped(); } } private function markAsSkippedIfFosRestBundleIsNotAvailable(): void { if (!class_exists(FOSRestBundle::class)) { $this->markTestSkipped('FriendsOfSymfony Rest Bundle is not installed.'); } } private function isRoutingPathBcLayerEnabled(): bool { return (bool) $this->getContainer()->getParameter('sylius.routing_path_bc_layer'); } private function markAsSkippedIfBcLayerIsEnabled(): void { if ($this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be disabled.'); } } private function markAsSkippedIfBcLayerIsNotEnabled(): void { if (!$this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be enabled.'); } } } ================================================ FILE: tests/Application/src/Tests/Controller/BoardGameUiTest.php ================================================ client = self::createClient(); } #[Test] public function it_allows_showing_a_board_game(): void { $boardGame = BoardGameFactory::new() ->withName(new BoardGameName('Ticket to Ride')) ->create() ; $this->client->request('GET', '/admin/board-games/' . $boardGame->id()); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $content = $response->getContent(); $this->assertStringContainsString(sprintf('ID: %s', $boardGame->id()), $content); $this->assertStringContainsString('Name: Ticket to Ride', $content); } #[Test] public function it_allows_browsing_board_games(): void { $stoneAgeBoardGame = BoardGameFactory::new() ->withName(new BoardGameName('Stone Age')) ->create() ; $ticketToRideBoardGame = BoardGameFactory::new() ->withName(new BoardGameName('Ticket to Ride')) ->create() ; $this->client->request('GET', '/admin/board-games'); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $content = $response->getContent(); $this->assertStringContainsString('Stone Age', $content); $this->assertStringContainsString(sprintf('Show', $stoneAgeBoardGame->id()), $content); $this->assertStringContainsString(sprintf('Edit', $stoneAgeBoardGame->id()), $content); $this->assertStringContainsString(sprintf('
', $stoneAgeBoardGame->id()), $content); $this->assertStringContainsString('Ticket to Ride', $content); $this->assertStringContainsString(sprintf('Show', $ticketToRideBoardGame->id()), $content); $this->assertStringContainsString(sprintf('Edit', $ticketToRideBoardGame->id()), $content); $this->assertStringContainsString(sprintf('', $ticketToRideBoardGame->id()), $content); } #[Test] public function it_allows_accessing_board_game_creation_page(): void { $this->client->request('GET', '/admin/board-games/new'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); } #[Test] public function it_allows_creating_a_board_game(): void { $this->client->request('GET', '/admin/board-games/new'); $this->client->submitForm('Create', [ 'board_game[name]' => 'Puerto Rico', ]); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); /** @var BoardGame $boardGame */ $boardGame = static::getContainer()->get(BoardGameRepositoryInterface::class)->findOneBy(['name.value' => 'Puerto Rico']); $this->assertNotNull($boardGame); $this->assertSame('Puerto Rico', (string) $boardGame->name()); } #[Test] public function it_does_not_allow_to_create_a_board_game_if_there_is_a_validation_error(): void { $this->client->request('GET', '/admin/board-games/new'); $this->client->submitForm('Create', [ 'board_game[name]' => null, ]); $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); } #[Test] public function it_allows_updating_a_board_game(): void { $boardGame = BoardGameFactory::createOne(); $this->client->request('GET', '/admin/board-games/' . $boardGame->id() . '/edit'); $this->client->submitForm('Save changes', [ 'board_game[name]' => 'Puerto Rico', ]); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); $boardGame = refresh($boardGame); $this->assertSame('Puerto Rico', (string) $boardGame->name()); } #[Test] public function it_does_not_allow_to_update_a_board_game_if_there_is_a_validation_error(): void { $boardGame = BoardGameFactory::createOne(); $this->client->request('GET', '/admin/board-games/' . $boardGame->id() . '/edit'); $this->client->submitForm('Save changes', [ 'board_game[name]' => null, ]); $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); } #[Test] public function it_allows_deleting_a_board_game(): void { BoardGameFactory::createOne(); $this->client->request('GET', '/admin/board-games'); $this->client->submitForm('Delete'); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); /** @var BoardGame[] $boardGames */ $boardGames = static::getContainer()->get(BoardGameRepositoryInterface::class)->findAll(); $this->assertEmpty($boardGames); } } ================================================ FILE: tests/Application/src/Tests/Controller/BookApiTest.php ================================================ markAsSkippedIfFosRestBundleIsNotAvailable(); } #[Test] public function it_allows_creating_a_book(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); $data = <<client->request('POST', '/books', [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "title":"Star Wars: Dark Disciple", "author":"Christie Golden" } JSON ); } #[Test] public function it_allows_creating_a_book_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); $data = <<client->request('POST', '/books/', [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "title":"Star Wars: Dark Disciple", "author":"Christie Golden" } JSON ); } #[Test] public function it_allows_updating_a_book(): void { $book = BookFactory::createOne(); $data = <<client->request('PUT', '/books/' . $book->getId(), [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT); $book = refresh($book); $enUsTranslation = $book->getTranslation('en_US'); $plPLTranslation = $book->getTranslation('pl_PL'); $this->assertInstanceOf(BookTranslation::class, $enUsTranslation); $this->assertInstanceOf(BookTranslation::class, $plPLTranslation); $this->assertEquals('Star Wars: Dark Disciple', $enUsTranslation->getTitle()); $this->assertEquals('Gwiezdne Wojny: Mroczny Uczeń', $plPLTranslation->getTitle()); $this->assertEquals('Christie Golden', $book->getAuthor()); } #[Test] public function it_allows_updating_partial_information_about_a_book(): void { $book = BookFactory::createOne(); $data = <<client->request('PATCH', '/books/' . $book->getId(), [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT); $book = refresh($book); $this->assertEquals('Christie Golden', $book->getAuthor()); } #[Test] public function it_allows_removing_a_book(): void { $book = BookFactory::createOne(); $bookId = $book->getId(); $this->client->request('DELETE', '/books/' . $book->getId()); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT); $this->assertEquals(0, $this->getContainer()->get('app.repository.book')->count([])); } #[Test] public function it_allows_showing_a_book(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); $book = BookFactory::new() ->withTranslations([ BookTranslationFactory::new() ->withLocale('en_US') ->withTitle('Lord of The Rings'), BookTranslationFactory::new() ->withLocale('pl_PL') ->withTitle('Władca Pierścieni'), ]) ->withAuthor('J.R.R. Tolkien') ->create() ; $this->client->request('GET', '/books/' . $book->getId()); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "title":"Lord of The Rings", "author":"J.R.R. Tolkien" } JSON ); } #[Test] public function it_allows_indexing_books(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); DefaultBooksStory::load(); $this->client->request('GET', '/books'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesDefaultBooksIndex(); } #[Test] public function it_allows_indexing_books_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); DefaultBooksStory::load(); $this->client->request('GET', '/books/'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesDefaultBooksIndex(); } #[Test] public function it_allows_paginating_the_index_of_books(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); MoreBooksStory::load(); $this->client->request('GET', '/books', ['page' => 2]); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesMoreBooksIndexPage2(); } #[Test] public function it_allows_paginating_the_index_of_books_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); MoreBooksStory::load(); $this->client->request('GET', '/books/', ['page' => 2]); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesMoreBooksIndexPage2(); } #[Test] public function it_does_not_allow_showing_resource_if_it_not_exists(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); $this->client->request('GET', '/books/3'); $this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND); } #[Test] public function it_does_not_apply_sorting_for_non_existing_field(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); MoreBooksStory::load(); $this->client->request('GET', '/sortable-books/', ['sorting' => ['name' => 'DESC']]); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesMoreBooksIndex(); } #[Test] public function it_does_not_apply_filtering_for_non_existing_field(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); MoreBooksStory::load(); $this->client->request('GET', '/filterable-books/', ['criteria' => ['name' => 'John']]); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesMoreBooksIndex(); } #[Test] public function it_applies_sorting_for_existing_field(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); MoreBooksStory::load(); $this->client->request('GET', '/sortable-books/', ['sorting' => ['id' => 'DESC']]); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseMatchesPattern( <<<'JSON' { "page": 1, "limit": 10, "pages": 3, "total": 22, "_links": { "first": { "href": "/sortable-books/?sorting%5Bid%5D=DESC&page=1&limit=10" }, "last": { "href": "/sortable-books/?sorting%5Bid%5D=DESC&page=3&limit=10" }, "next": { "href": "/sortable-books/?sorting%5Bid%5D=DESC&page=2&limit=10" }, "self": { "href": "/sortable-books/?sorting%5Bid%5D=DESC&page=1&limit=10" } }, "_embedded": { "items": [ { "author": "@string@", "id": @integer@, "title": "Book 22" }, { "author": "@string@", "id": @integer@, "title": "Book 21" }, { "author": "@string@", "id": @integer@, "title": "Book 20" }, { "author": "@string@", "id": @integer@, "title": "Book 19" }, { "author": "@string@", "id": @integer@, "title": "Book 18" }, { "author": "@string@", "id": @integer@, "title": "Book 17" }, { "author": "@string@", "id": @integer@, "title": "Book 16" }, { "author": "@string@", "id": @integer@, "title": "Book 15" }, { "author": "@string@", "id": @integer@, "title": "Book 14" }, { "author": "@string@", "id": @integer@, "title": "Book 13" } ] } } JSON ); } #[Test] public function it_applies_filtering_for_existing_field(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); MoreBooksStory::load(); BookFactory::new()->withAuthor('J.R.R. Tolkien')->create(); $this->client->request('GET', '/filterable-books/', ['criteria' => ['author' => 'J.R.R. Tolkien']]); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseMatchesPattern( <<<'JSON' { "page": 1, "limit": 10, "pages": 1, "total": 1, "_links": { "self": { "href": "\/filterable-books\/?criteria%5Bauthor%5D=J.R.R.%20Tolkien&page=1&limit=10" }, "first": { "href": "\/filterable-books\/?criteria%5Bauthor%5D=J.R.R.%20Tolkien&page=1&limit=10" }, "last": { "href": "\/filterable-books\/?criteria%5Bauthor%5D=J.R.R.%20Tolkien&page=1&limit=10" } }, "_embedded": { "items": [ { "id": @integer@, "author": "J.R.R. Tolkien" } ] } } JSON ); } #[Test] public function it_allows_creating_a_book_via_custom_factory(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); $data = <<<'JSON' { "translations": { "en_US": { "title": "Star Wars: Dark Disciple" } }, "author": "Christie Golden" } JSON ; $this->client->request('POST', '/create-custom-book', [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "title":"Star Wars: Dark Disciple", "author":"Christie Golden" } JSON ); } #[Test] public function it_allows_indexing_books_via_custom_repository(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); DefaultBooksStory::load(); $this->client->request('GET', '/find-custom-books'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesDefaultBooksIndex(); } #[Test] public function it_allows_showing_a_book_via_custom_repository(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); DefaultBooksStory::load(); $this->client->request('GET', '/find-custom-book'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "title":"Lord of The Rings", "author":"J.R.R. Tolkien" } JSON ); } private function assertResponseMatchesDefaultBooksIndex(): void { $this->assertResponseMatchesPattern( <<<'JSON' { "page": 1, "limit": 10, "pages": 1, "total": 2, "_links": { "self": { "href": "@string@" }, "first": { "href": "@string@" }, "last": { "href": "@string@" } }, "_embedded": { "items": [ { "id": @integer@, "title": "Lord of The Rings", "author": "J.R.R. Tolkien" }, { "id": @integer@, "title": "Game of Thrones", "author": "George R. R. Martin" } ] } } JSON ); } private function assertResponseMatchesMoreBooksIndex(): void { $this->assertResponseMatchesPattern( <<<'JSON' { "page": 1, "limit": 10, "pages": 3, "total": 22, "_links": { "self": { "href": "@string@" }, "first": { "href": "@string@" }, "last": { "href": "@string@" }, "next": { "href": "@string@" } }, "_embedded": { "items": [ { "id": @integer@, "title": "Book 1", "author": "@string@" }, { "id": @integer@, "title": "Book 2", "author": "@string@" }, { "id": @integer@, "title": "Book 3", "author": "@string@" }, { "id": @integer@, "title": "Book 4", "author": "@string@" }, { "id": @integer@, "title": "Book 5", "author": "@string@" }, { "id": @integer@, "title": "Book 6", "author": "@string@" }, { "id": @integer@, "title": "Book 7", "author": "@string@" }, { "id": @integer@, "title": "Book 8", "author": "@string@" }, { "id": @integer@, "title": "Book 9", "author": "@string@" }, { "id": @integer@, "title": "Book 10", "author": "@string@" } ] } } JSON ); } private function assertResponseMatchesMoreBooksIndexPage2(): void { $this->assertResponseMatchesPattern( <<<'JSON' { "page": 2, "limit": 10, "pages": 3, "total": 22, "_links": { "self": { "href": "@string@" }, "first": { "href": "@string@" }, "last": { "href": "@string@" }, "next": { "href": "@string@" }, "previous": { "href": "@string@" } }, "_embedded": { "items": [ { "id": @integer@, "title": "Book 11", "author": "@string@" }, { "id": @integer@, "title": "Book 12", "author": "@string@" }, { "id": @integer@, "title": "Book 13", "author": "@string@" }, { "id": @integer@, "title": "Book 14", "author": "@string@" }, { "id": @integer@, "title": "Book 15", "author": "@string@" }, { "id": @integer@, "title": "Book 16", "author": "@string@" }, { "id": @integer@, "title": "Book 17", "author": "@string@" }, { "id": @integer@, "title": "Book 18", "author": "@string@" }, { "id": @integer@, "title": "Book 19", "author": "@string@" }, { "id": @integer@, "title": "Book 20", "author": "@string@" } ] } } JSON ); } private function markAsSkippedIfHateoasIsNotAvailable(): void { if (!class_exists(BazingaHateoasBundle::class)) { $this->markTestSkipped('HateoasBundle is not installed.'); } } private function markAsSkippedIfFosRestBundleIsNotAvailable(): void { if (!class_exists(FOSRestBundle::class)) { $this->markTestSkipped('FriendsOfSymfony Rest Bundle is not installed.'); } } private function isRoutingPathBcLayerEnabled(): bool { return (bool) $this->getContainer()->getParameter('sylius.routing_path_bc_layer'); } private function markAsSkippedIfBcLayerIsEnabled(): void { if ($this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be disabled.'); } } private function markAsSkippedIfBcLayerIsNotEnabled(): void { if (!$this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be enabled.'); } } } ================================================ FILE: tests/Application/src/Tests/Controller/ComicBookApiTest.php ================================================ markAsSkippedIfFosRestBundleIsNotAvailable(); $this->markAsSkippedIfJMSSerializerBundleIsNotAvailable(); } #[Test] public function it_allows_creating_a_comic_book(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $data = <<<'JSON' { "title": "Deadpool #1-69", "author": { "firstName": "Joe", "lastName": "Kelly" } } JSON ; $this->client->request('POST', '/v1/comic-books', [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "author": { "first_name": "Joe", "last_name": "Kelly" }, "title": "Deadpool #1-69" } JSON ); } #[Test] public function it_allows_creating_a_comic_book_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $data = <<<'JSON' { "title": "Deadpool #1-69", "author": { "firstName": "Joe", "lastName": "Kelly" } } JSON ; $this->client->request('POST', '/v1/comic-books/', [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "author": { "first_name": "Joe", "last_name": "Kelly" }, "title": "Deadpool #1-69" } JSON ); } #[Test] public function it_allows_versioned_creating_a_comic_book(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); $data = <<<'JSON' { "title": "Deadpool #1-69", "author": { "firstName": "Joe", "lastName": "Kelly" } } JSON ; $this->client->request('POST', '/v1.2/comic-books', [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "author_first_name": "Joe", "author_last_name": "Kelly", "title": "Deadpool #1-69" } JSON ); } #[Test] public function it_allows_versioned_creating_a_comic_book_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); $data = <<<'JSON' { "title": "Deadpool #1-69", "author": { "firstName": "Joe", "lastName": "Kelly" } } JSON ; $this->client->request('POST', '/v1.2/comic-books/', [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "author_first_name": "Joe", "author_last_name": "Kelly", "title": "Deadpool #1-69" } JSON ); } #[Test] public function it_allows_updating_a_comic_book(): void { $comicBook = self::someComicBook()->create(); $data = <<client->request('PUT', '/v1/comic-books/' . $comicBook->getId(), [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT); } #[Test] public function it_allows_updating_partial_information_about_a_comic_book(): void { $comicBook = self::someComicBook()->create(); $data = <<client->request('PATCH', '/v1/comic-books/' . $comicBook->getId(), [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT); $book = $this->getContainer()->get('app.repository.comic_book')->find($comicBook->getId()); $this->getContainer()->get('doctrine.orm.entity_manager')->refresh($book); $this->assertEquals('Joe', $book->getAuthor()->getFirstName()); $this->assertEquals('Kelly', $book->getAuthor()->getLastName()); } #[Test] public function it_allows_removing_a_comic_book(): void { $comicBook = self::someComicBook()->create(); $this->client->request('DELETE', '/v1/comic-books/' . $comicBook->getId()); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT); } #[Test] public function it_allows_showing_a_comic_book(): void { $comicBook = self::someComicBook()->create(); $this->client->request('GET', '/v1/comic-books/' . $comicBook->getId()); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "author": { "first_name": "Andrea", "last_name": "Sorrentino" }, "title": "Old Man Logan" } JSON ); } #[Test] public function it_allows_versioning_of_a_showing_comic_book_serialization(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); $comicBook = self::someComicBook()->create(); $this->client->request('GET', '/v1.2/comic-books/' . $comicBook->getId()); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "author_first_name": "Andrea", "author_last_name": "Sorrentino", "title": "Old Man Logan" } JSON ); } #[Test] public function it_allows_indexing_of_comic_books(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); DefaultComicBooksStory::load(); $this->client->request('GET', '/v1/comic-books'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "page": 1, "limit": 10, "pages": 1, "total": 2, "_links": { "self": { "href": "\/v1\/comic-books?page=1&limit=10" }, "first": { "href": "\/v1\/comic-books?page=1&limit=10" }, "last": { "href": "\/v1\/comic-books?page=1&limit=10" } }, "_embedded": { "items": [ { "id": @integer@, "author": { "first_name": "Andrea", "last_name": "Sorrentino" }, "title": "Old Man Logan" }, { "id": @integer@, "author": { "first_name": "Brian Michael", "last_name": "Bendis" }, "title": "Civil War II" } ] } } JSON ); } #[Test] public function it_allows_indexing_of_comic_books_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); DefaultComicBooksStory::load(); $this->client->request('GET', '/v1/comic-books/'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "page": 1, "limit": 10, "pages": 1, "total": 2, "_links": { "self": { "href": "\/v1\/comic-books\/?page=1&limit=10" }, "first": { "href": "\/v1\/comic-books\/?page=1&limit=10" }, "last": { "href": "\/v1\/comic-books\/?page=1&limit=10" } }, "_embedded": { "items": [ { "id": @integer@, "author": { "first_name": "Andrea", "last_name": "Sorrentino" }, "title": "Old Man Logan" }, { "id": @integer@, "author": { "first_name": "Brian Michael", "last_name": "Bendis" }, "title": "Civil War II" } ] } } JSON ); } #[Test] public function it_allows_versioned_indexing_of_comic_books(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); DefaultComicBooksStory::load(); $this->client->request('GET', 'v1.2/comic-books'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "page": 1, "limit": 10, "pages": 1, "total": 2, "_links": { "self": { "href": "\/v1.2\/comic-books?page=1&limit=10" }, "first": { "href": "\/v1.2\/comic-books?page=1&limit=10" }, "last": { "href": "\/v1.2\/comic-books?page=1&limit=10" } }, "_embedded": { "items": [ { "id": @integer@, "author_first_name": "Andrea", "author_last_name": "Sorrentino", "title": "Old Man Logan" }, { "id": @integer@, "author_first_name": "Brian Michael", "author_last_name": "Bendis", "title": "Civil War II" } ] } } JSON ); } #[Test] public function it_allows_versioned_indexing_of_comic_books_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $this->markAsSkippedIfHateoasIsNotAvailable(); DefaultComicBooksStory::load(); $this->client->request('GET', '/v1.2/comic-books/'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<client->request('GET', '/v1/comic-books/3'); $this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND); } private static function someComicBook(): ComicBookFactory { return ComicBookFactory::new() ->withTitle('Old Man Logan') ->withAuthor( AuthorFactory::new() ->withFirstName('Andrea') ->withLastName('Sorrentino'), ) ; } private function isRoutingPathBcLayerEnabled(): bool { return (bool) $this->getContainer()->getParameter('sylius.routing_path_bc_layer'); } private function markAsSkippedIfBcLayerIsEnabled(): void { if ($this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be disabled.'); } } private function markAsSkippedIfBcLayerIsNotEnabled(): void { if (!$this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be enabled.'); } } private function markAsSkippedIfHateoasIsNotAvailable(): void { if (!class_exists(BazingaHateoasBundle::class)) { $this->markTestSkipped('HateoasBundle is not installed.'); } } private function markAsSkippedIfFosRestBundleIsNotAvailable(): void { if (!class_exists(FOSRestBundle::class)) { $this->markTestSkipped('FriendsOfSymfony Rest Bundle is not installed.'); } } private function markAsSkippedIfJMSSerializerBundleIsNotAvailable(): void { if (!class_exists(JMSSerializerBundle::class)) { $this->markTestSkipped('JMS Serializer Bundle is not installed.'); } } } ================================================ FILE: tests/Application/src/Tests/Controller/GedmoApiTest.php ================================================ markAsSkippedIfBcLayerIsEnabled(); $this->markAsSkippedIfGedmoDoctrineExtensionsIsNotAvailable(); $data = <<client->request('POST', '/gedmos', [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "position": 0, "extra": "Some info" } JSON ); } #[Test] public function it_allows_creating_a_comic_book_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $this->markAsSkippedIfGedmoDoctrineExtensionsIsNotAvailable(); $data = <<client->request('POST', '/gedmos/', [], [], ['CONTENT_TYPE' => 'application/json'], $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "position": 0, "extra": "Some info" } JSON ); } private function isRoutingPathBcLayerEnabled(): bool { return (bool) $this->getContainer()->getParameter('sylius.routing_path_bc_layer'); } private function markAsSkippedIfBcLayerIsEnabled(): void { if ($this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be disabled.'); } } private function markAsSkippedIfBcLayerIsNotEnabled(): void { if (!$this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be enabled.'); } } private function markAsSkippedIfGedmoDoctrineExtensionsIsNotAvailable(): void { if (!class_exists(SortableListener::class)) { $this->markTestSkipped('Gedmo Doctrine Extension is not available.'); } } } ================================================ FILE: tests/Application/src/Tests/Controller/PullRequestApiTest.php ================================================ markAsSkippedIfNoStateMachineIsAvailable(); $this->markAsSkippedIfFosRestBundleIsNotAvailable(); } #[Test] public function it_allows_creating_a_pull_request(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $this->client->request('POST', '/pull-requests', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "current_place": "start" } JSON ); } #[Test] public function it_allows_creating_a_pull_request_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $this->client->request('POST', '/pull-requests/', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "current_place": "start" } JSON ); } #[Test] public function it_allows_submitting_a_pull_request(): void { $pullRequest = PullRequestFactory::new() ->withCurrentPlace('start') ->create() ; $this->client->request('PUT', '/pull-requests/' . $pullRequest->getId() . '/submit', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "current_place": "test" } JSON ); } #[Test] public function it_allows_waiting_for_review_a_pull_request(): void { $pullRequest = PullRequestFactory::new() ->withCurrentPlace('test') ->create() ; $this->client->request('PUT', '/pull-requests/' . $pullRequest->getId() . '/wait_for_review', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json'); $this->assertResponseMatchesPattern( <<<'JSON' { "id": @integer@, "current_place": "review" } JSON ); } #[Test] public function it_does_not_allow_to_wait_for_review_on_pull_request_with_start_status(): void { $pullRequest = PullRequestFactory::new() ->withCurrentPlace('start') ->create() ; $this->client->request('PUT', '/pull-requests/' . $pullRequest->getId() . '/wait_for_review', [], [], ['CONTENT_TYPE' => 'application/json'], '{}'); $this->assertResponseStatusCodeSame(Response::HTTP_BAD_REQUEST); $this->assertResponseHeaderSame('content-type', 'application/json'); } private function isRoutingPathBcLayerEnabled(): bool { return (bool) $this->getContainer()->getParameter('sylius.routing_path_bc_layer'); } private function markAsSkippedIfBcLayerIsEnabled(): void { if ($this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be disabled.'); } } private function markAsSkippedIfBcLayerIsNotEnabled(): void { if (!$this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be enabled.'); } } private function markAsSkippedIfFosRestBundleIsNotAvailable(): void { if (!class_exists(FOSRestBundle::class)) { $this->markTestSkipped('FriendsOfSymfony Rest Bundle is not installed.'); } } private function markAsSkippedIfNoStateMachineIsAvailable(): void { if (!class_exists(Registry::class) || !class_exists(winzouStateMachineBundle::class)) { $this->markTestSkipped('No state machine available.'); } } } ================================================ FILE: tests/Application/src/Tests/Controller/ScienceBookUiTest.php ================================================ client = self::createClient(); } #[Test] public function it_allows_showing_a_book(): void { $scienceBook = ScienceBookFactory::new() ->withTitle('A Brief History of Time') ->withAuthor( AuthorFactory::new() ->withFirstName('Stephen') ->withLastName('Hawking'), ) ->create() ; $this->client->request('GET', '/science-books/' . $scienceBook->getId()); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $content = $response->getContent(); $this->assertStringContainsString(sprintf('ID: %d', $scienceBook->getId()), $content); $this->assertStringContainsString('Title: A Brief History of Time', $content); $this->assertStringContainsString('Author: Stephen Hawking', $content); } #[Test] public function it_allows_indexing_books(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $firstBook = ScienceBookFactory::new() ->withTitle('A Brief History of Time') ->withAuthor( AuthorFactory::new() ->withFirstName('Stephen') ->withLastName('Hawking'), ) ->create() ; $secondBook = ScienceBookFactory::new() ->withTitle('The Future of Humanity') ->withAuthor( AuthorFactory::new() ->withFirstName('Michio') ->withLastName('Kaku'), ) ->create() ; $this->client->request('GET', '/science-books'); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $content = $response->getContent(); $this->assertStringContainsString('

Books

', $content); $this->assertStringContainsString( sprintf('%dA Brief History of TimeStephen Hawking', $firstBook->getId()), $content, ); $this->assertStringContainsString( sprintf('%dThe Future of HumanityMichio Kaku', $secondBook->getId()), $content, ); } #[Test] public function it_allows_indexing_books_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $firstBook = ScienceBookFactory::new() ->withTitle('A Brief History of Time') ->withAuthor( AuthorFactory::new() ->withFirstName('Stephen') ->withLastName('Hawking'), ) ->create() ; $secondBook = ScienceBookFactory::new() ->withTitle('The Future of Humanity') ->withAuthor( AuthorFactory::new() ->withFirstName('Michio') ->withLastName('Kaku'), ) ->create() ; $this->client->request('GET', '/science-books/'); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $content = $response->getContent(); $this->assertStringContainsString('

Books

', $content); $this->assertStringContainsString( sprintf('%dA Brief History of TimeStephen Hawking', $firstBook->getId()), $content, ); $this->assertStringContainsString( sprintf('%dThe Future of HumanityMichio Kaku', $secondBook->getId()), $content, ); } #[Test] public function it_allows_creating_a_book(): void { $newBookTitle = 'The Book of Why'; $newBookAuthorFirstName = 'Judea'; $newBookAuthorLastName = 'Pearl'; $this->client->request('GET', '/science-books/new'); $this->client->submitForm('Create', [ 'science_book[title]' => $newBookTitle, 'science_book[author][firstName]' => $newBookAuthorFirstName, 'science_book[author][lastName]' => $newBookAuthorLastName, ]); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); /** @var ScienceBook $book */ $book = static::getContainer()->get('app.repository.science_book')->findOneBy(['title' => $newBookTitle]); $this->assertNotNull($book); $this->assertSame($newBookTitle, $book->getTitle()); $this->assertSame($newBookAuthorFirstName, $book->getAuthorFirstName()); $this->assertSame($newBookAuthorLastName, $book->getAuthorLastName()); } #[Test] public function it_does_not_allow_to_create_a_book_if_there_is_a_validation_error(): void { $newBookTitle = 'The Book of Why'; $newBookAuthorLastName = 'Pearl'; $this->client->request('GET', '/science-books/new'); $this->client->submitForm('Create', [ 'science_book[title]' => $newBookTitle, 'science_book[author][lastName]' => $newBookAuthorLastName, ]); $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); /** @var ScienceBook $book */ $book = static::getContainer()->get('app.repository.science_book')->findOneBy(['title' => $newBookTitle]); $this->assertNull($book); } #[Test] public function it_allows_updating_a_book(): void { $scienceBook = ScienceBookFactory::createOne(); $newBookTitle = 'The Book of Why'; $newBookAuthorFirstName = 'Judea'; $newBookAuthorLastName = 'Pearl'; $this->client->request('GET', '/science-books/' . $scienceBook->getId() . '/edit'); $this->client->submitForm('Save changes', [ 'science_book[title]' => $newBookTitle, 'science_book[author][firstName]' => $newBookAuthorFirstName, 'science_book[author][lastName]' => $newBookAuthorLastName, ]); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); $scienceBook = refresh($scienceBook); $this->assertSame($newBookTitle, $scienceBook->getTitle()); $this->assertSame($newBookAuthorFirstName, $scienceBook->getAuthorFirstName()); $this->assertSame($newBookAuthorLastName, $scienceBook->getAuthorLastName()); } #[Test] public function it_does_not_allow_to_update_a_book_if_there_is_a_validation_error(): void { $scienceBook = ScienceBookFactory::new() ->withTitle('The Shinning') ->create(); $newBookTitle = 'The Book of Why'; $newBookAuthorLastName = 'Pearl'; $this->client->request('GET', '/science-books/' . $scienceBook->getId() . '/edit'); $this->client->submitForm('Save changes', [ 'science_book[title]' => $newBookTitle, 'science_book[author][firstName]' => null, 'science_book[author][lastName]' => $newBookAuthorLastName, ]); $this->assertResponseStatusCodeSame(expectedCode: Response::HTTP_UNPROCESSABLE_ENTITY); /** @var ScienceBook $book */ $book = static::getContainer()->get('app.repository.science_book')->find($scienceBook->getId()); $this->getContainer()->get('doctrine.orm.entity_manager')->refresh($book); $this->assertNotEquals($newBookTitle, $book->getTitle()); } #[Test] public function it_allows_deleting_a_book(): void { $this->markAsSkippedIfBcLayerIsEnabled(); ScienceBookFactory::createOne(); $this->client->request('GET', '/science-books'); $this->client->submitForm('Delete'); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); /** @var ScienceBook[] $books */ $books = static::getContainer()->get('app.repository.science_book')->findAll(); $this->assertEmpty($books); } #[Test] public function it_allows_deleting_a_book_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); ScienceBookFactory::createOne(); $this->client->request('GET', '/science-books/'); $this->client->submitForm('Delete'); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); /** @var ScienceBook[] $books */ $books = static::getContainer()->get('app.repository.science_book')->findAll(); $this->assertEmpty($books); } #[Test] public function it_allows_filtering_books(): void { $this->markAsSkippedIfBcLayerIsEnabled(); $firstBook = ScienceBookFactory::new() ->withTitle('A Brief History of Time') ->withAuthor( AuthorFactory::new() ->withFirstName('Stephen') ->withLastName('Hawking'), ) ->create() ; $secondBook = ScienceBookFactory::new() ->withTitle('The Future of Humanity') ->withAuthor( AuthorFactory::new() ->withFirstName('Michio') ->withLastName('Kaku'), ) ->create() ; $this->client->request('GET', '/science-books?criteria[search][value]=history of time'); $response = $this->client->getResponse(); $this->assertResponseStatusCodeSame(expectedCode: Response::HTTP_OK); $content = $response->getContent(); $this->assertStringContainsString('

Books

', $content); $this->assertStringContainsString( sprintf('%dA Brief History of TimeStephen Hawking', $firstBook->getId()), $content, ); $this->assertStringNotContainsString( sprintf('%dThe Future of HumanityMichio Kaku', $secondBook->getId()), $content, ); } #[Test] public function it_allows_filtering_books_with_bc_layer(): void { $this->markAsSkippedIfBcLayerIsNotEnabled(); $firstBook = ScienceBookFactory::new() ->withTitle('A Brief History of Time') ->withAuthor( AuthorFactory::new() ->withFirstName('Stephen') ->withLastName('Hawking'), ) ->create() ; $secondBook = ScienceBookFactory::new() ->withTitle('The Future of Humanity') ->withAuthor( AuthorFactory::new() ->withFirstName('Michio') ->withLastName('Kaku'), ) ->create() ; $this->client->request('GET', '/science-books/?criteria[search][value]=history of time'); $response = $this->client->getResponse(); $this->assertResponseStatusCodeSame(expectedCode: Response::HTTP_OK); $content = $response->getContent(); $this->assertStringContainsString('

Books

', $content); $this->assertStringContainsString( sprintf('%dA Brief History of TimeStephen Hawking', $firstBook->getId()), $content, ); $this->assertStringNotContainsString( sprintf('%dThe Future of HumanityMichio Kaku', $secondBook->getId()), $content, ); } private function isRoutingPathBcLayerEnabled(): bool { return (bool) $this->getContainer()->getParameter('sylius.routing_path_bc_layer'); } private function markAsSkippedIfBcLayerIsEnabled(): void { if ($this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be disabled.'); } } private function markAsSkippedIfBcLayerIsNotEnabled(): void { if (!$this->isRoutingPathBcLayerEnabled()) { $this->markTestSkipped('This test requires The BC layer to be enabled.'); } } } ================================================ FILE: tests/Application/src/Tests/Controller/SpeakerUiTest.php ================================================ client = $this->createClient(); } /** @test */ public function it_allows_browsing_speakers(): void { SpeakerFactory::new() ->withFirstName('Francis') ->withLastName('Hilaire') ->create() ; SpeakerFactory::new() ->withFirstName('Gregor') ->withLastName('Šink') ->create() ; $this->client->request('GET', '/admin/speakers'); $response = $this->client->getResponse(); $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); $content = $response->getContent(); $this->assertStringContainsString('Francis Hilaire', $content); $this->assertStringContainsString('Gregor Šink', $content); } /** @test */ public function it_allows_accessing_speaker_creation_page(): void { $this->client->request('GET', '/admin/speakers/new'); $response = $this->client->getResponse(); $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); } /** @test */ public function it_allows_creating_a_speaker(): void { $this->client->request('GET', '/admin/speakers/new'); $this->client->submitForm('Create', [ 'speaker[firstName]' => 'Francis', 'speaker[lastName]' => 'Hilaire', ]); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); /** @var Speaker|null $speaker */ $speaker = static::getContainer()->get(EntityManagerInterface::class)->getRepository(Speaker::class)->findOneBy(['firstName' => 'Francis']); $this->assertNotNull($speaker); $this->assertSame('Francis Hilaire', $speaker->getFullName()); } /** @test */ public function it_allows_updating_a_speaker(): void { $speaker = SpeakerFactory::createOne(); $this->client->request('GET', '/admin/speakers/' . $speaker->getId() . '/edit'); $this->client->submitForm('Save changes', [ 'speaker[firstName]' => 'Francis', 'speaker[lastName]' => 'Hilaire', ]); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); $speaker = refresh($speaker); $this->assertSame('Francis Hilaire', $speaker->getFullName()); } /** @test */ public function it_allows_deleting_a_speaker(): void { SpeakerFactory::createOne(); $this->client->request('GET', '/admin/speakers'); $this->client->submitForm('Delete'); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); /** @var Speaker[] $speakers */ $speakers = static::getContainer()->get(EntityManagerInterface::class)->getRepository(Speaker::class)->findAll(); $this->assertEmpty($speakers); } } ================================================ FILE: tests/Application/src/Tests/Controller/SubscriptionJsonApiTest.php ================================================ 'application/json', 'HTTP_ACCEPT' => 'application/json', ]; #[Test] public function it_allows_showing_a_subscription(): void { $subscription = SubscriptionFactory::new() ->withEmail('marty.mcfly@bttf.com') ->create() ; $this->client->request(method: 'GET', uri: '/ajax/subscriptions/' . $subscription->getId(), server: self::$headers); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json; charset=utf-8'); $this->assertResponseMatchesPattern( <<<'JSON' { "state": "new", "email": "marty.mcfly@bttf.com" } JSON ); } #[Test] public function it_allows_indexing_subscriptions(): void { DefaultSubscriptionsStory::load(); $this->client->request(method: 'GET', uri: '/ajax/subscriptions', server: self::$headers); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/json; charset=utf-8'); $this->assertResponseMatchesPattern( <<<'JSON' { "items": [ { "state": "new", "email": "marty.mcfly@bttf.com" }, { "state": "new", "email": "doc.brown@bttf.com" }, { "state": "accepted", "email": "biff.tannen@bttf.com" }, { "state": "new", "email": "lorraine.baines@bttf.com" }, { "state": "new", "email": "george.mcfly@bttf.com" }, { "state": "new", "email": "jennifer.parker@bttf.com" } ], "pagination": { "current_page": 1, "has_previous_page": false, "has_next_page": false, "per_page": 10, "total_items": 6, "total_pages": 1 } } JSON ); } #[Test] public function it_allows_creating_a_subscription(): void { $data = <<<'JSON' { "email": "marty.mcfly@bttf.com" } JSON ; $this->client->request(method: 'POST', uri: '/ajax/subscriptions', server: self::$headers, content: $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'application/json; charset=utf-8'); $this->assertResponseMatchesPattern( <<<'JSON' { "state": "new", "email": "marty.mcfly@bttf.com" } JSON ); } #[Test] public function it_does_not_allow_to_create_a_subscription_if_there_is_a_validation_error(): void { $data = <<<'JSON' { "email": "" } JSON ; $this->client->request(method: 'POST', uri: '/ajax/subscriptions', server: self::$headers, content: $data); $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); $this->assertResponseHeaderSame('content-type', 'application/json; charset=utf-8'); if (Kernel::VERSION_ID < 60400) { $this->assertResponseMatchesPattern( <<<'JSON' { "type": "https://symfony.com/errors/validation", "title": "Validation Failed", "detail": "email: This value should not be blank.", "violations": [ { "propertyPath": "email", "title": "This value should not be blank.", "parameters": { "{{ value }}": "\"\"" }, "type": "urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3" } ] } JSON ); return; } $this->assertResponseMatchesPattern( <<<'JSON' { "type": "https://symfony.com/errors/validation", "title": "Validation Failed", "detail": "email: This value should not be blank.", "violations": [ { "propertyPath": "email", "title": "This value should not be blank.", "template": "This value should not be blank.", "parameters": { "{{ value }}": "\"\"" }, "type": "urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3" } ] } JSON ); } #[Test] public function it_allows_updating_a_subscription(): void { $subscription = SubscriptionFactory::createOne(); $data = <<<'JSON' { "email": "calvin.klein@bttf.com" } JSON ; $this->client->request(method: 'PUT', uri: '/ajax/subscriptions/' . $subscription->getId(), server: self::$headers, content: $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT); } #[Test] public function it_does_not_allow_to_update_a_subscription_if_there_is_a_validation_error(): void { $subscription = SubscriptionFactory::createOne(); $data = <<client->request(method: 'PUT', uri: '/ajax/subscriptions/' . $subscription->getId(), server: self::$headers, content: $data); $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); $this->assertResponseHeaderSame('content-type', 'application/json; charset=utf-8'); if (Kernel::VERSION_ID < 60400) { $this->assertResponseMatchesPattern( <<<'JSON' { "type": "https://symfony.com/errors/validation", "title": "Validation Failed", "detail": "email: This value should not be blank.", "violations": [ { "propertyPath": "email", "title": "This value should not be blank.", "parameters": { "{{ value }}": "\"\"" }, "type": "urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3" } ] } JSON ); return; } $this->assertResponseMatchesPattern( <<<'JSON' { "type": "https://symfony.com/errors/validation", "title": "Validation Failed", "detail": "email: This value should not be blank.", "violations": [ { "propertyPath": "email", "title": "This value should not be blank.", "template": "This value should not be blank.", "parameters": { "{{ value }}": "\"\"" }, "type": "urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3" } ] } JSON ); } #[Test] public function it_allows_removing_a_subscription(): void { $subscription = SubscriptionFactory::createOne(); $this->client->request(method: 'DELETE', uri: '/ajax/subscriptions/' . $subscription->getId(), server: self::$headers); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT); } } ================================================ FILE: tests/Application/src/Tests/Controller/SubscriptionUiTest.php ================================================ client = self::createClient(); } #[Test] public function it_allows_showing_a_subscription(): void { $subscription = SubscriptionFactory::new() ->withEmail('marty.mcfly@bttf.com') ->create() ; $this->client->request('GET', '/admin/subscriptions/' . $subscription->getId()); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $content = $response->getContent(); $this->assertStringContainsString(sprintf('ID: %s', $subscription->getId()), $content); $this->assertStringContainsString('Email: marty.mcfly@bttf.com', $content); $this->assertStringContainsString('Foo: bar', $content); } #[Test] public function it_allows_browsing_subscriptions(): void { DefaultSubscriptionsStory::load(); $docBrownSubscription = SubscriptionFactory::find(['email' => 'doc.brown@bttf.com']); $biffTannenSubscription = SubscriptionFactory::find(['email' => 'biff.tannen@bttf.com']); $this->client->request('GET', '/admin/subscriptions'); $response = $this->client->getResponse(); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $content = $response->getContent(); // only 5 subscriptions if (method_exists($this, 'assertSelectorCount')) { $this->assertSelectorCount(5, 'tbody tr'); } $this->assertStringContainsString('doc.brown@bttf.com', $content); $this->assertStringContainsString(sprintf('Show', $docBrownSubscription->getId()), $content); $this->assertStringContainsString(sprintf('Edit', $docBrownSubscription->getId()), $content); $this->assertStringContainsString(sprintf('', $docBrownSubscription->getId()), $content); $this->assertStringContainsString('biff.tannen@bttf.com', $content); $this->assertStringContainsString(sprintf('Show', $biffTannenSubscription->getId()), $content); $this->assertStringContainsString(sprintf('Edit', $biffTannenSubscription->getId()), $content); $this->assertStringContainsString(sprintf('', $biffTannenSubscription->getId()), $content); } #[Test] public function it_allows_browsing_subscriptions_with_page_limit(): void { DefaultSubscriptionsStory::load(); $this->client->request('GET', '/admin/subscriptions?limit=3'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); // only 2 subscriptions if (method_exists($this, 'assertSelectorCount')) { $this->assertSelectorCount(3, 'tbody tr'); } } #[Test] public function it_allows_browsing_subscriptions_with_grid_limits(): void { DefaultSubscriptionsStory::load(); $this->client->request('GET', '/admin/subscriptions'); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); // only 5 subscriptions if (method_exists($this, 'assertSelectorCount')) { $this->assertSelectorCount(5, 'tbody tr'); } } #[Test] public function it_allows_creating_a_subscription(): void { $this->client->request('GET', '/admin/subscriptions/new'); $response = $this->client->getResponse(); $content = $response->getContent(); $this->assertStringContainsString('value="new@example.com"', $content); $this->client->submitForm('Create', [ 'subscription[email]' => 'biff.tannen@bttf.com', ]); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); /** @var Subscription $subscription */ $subscription = static::getContainer()->get('app.repository.subscription')->findOneBy(['email' => 'biff.tannen@bttf.com']); $this->assertNotNull($subscription); $this->assertSame('biff.tannen@bttf.com', (string) $subscription->email); } #[Test] public function it_does_not_allow_to_create_a_subscription_if_there_is_a_validation_error(): void { $this->client->request('GET', '/admin/subscriptions/new'); $this->client->submitForm('Create', [ 'subscription[email]' => null, ]); $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); } #[Test] public function it_allows_updating_a_subscription(): void { $subscription = SubscriptionFactory::createOne(); $this->client->request('GET', '/admin/subscriptions/' . $subscription->getId() . '/edit'); $this->client->submitForm('Save changes', [ 'subscription[email]' => 'biff.tannen@bttf.com', ]); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); $subscription = refresh($subscription); $this->assertSame('biff.tannen@bttf.com', (string) $subscription->email); } #[Test] public function it_does_not_allow_to_update_a_subscription_if_there_is_a_validation_error(): void { $subscription = SubscriptionFactory::createOne(); $this->client->request('GET', '/admin/subscriptions/' . $subscription->getId() . '/edit'); $this->client->submitForm('Save changes', [ 'subscription[email]' => null, ]); $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); } #[Test] public function it_allows_deleting_a_subscription(): void { SubscriptionFactory::createOne(); $this->client->request('GET', '/admin/subscriptions'); $this->client->submitForm('Delete'); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); /** @var Subscription[] $subscriptions */ $subscriptions = static::getContainer()->get('app.repository.subscription')->findAll(); $this->assertEmpty($subscriptions); } #[Test] public function it_allows_deleting_multiple_subscriptions(): void { DefaultSubscriptionsStory::load(); $this->client->request('GET', '/admin/subscriptions?limit=10'); $this->client->submitForm('Bulk delete'); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); /** @var Subscription[] $subscriptions */ $subscriptions = static::getContainer()->get('app.repository.subscription')->findAll(); $this->assertEmpty($subscriptions); } #[Test] public function it_allows_accepting_a_subscription(): void { $this->markAsSkippedIfNoStateMachineIsAvailable(); $subscription = SubscriptionFactory::createOne(); $this->client->request('GET', '/admin/subscriptions'); $this->client->submitForm('Accept'); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); $subscription = refresh($subscription); $this->assertSame('accepted', $subscription->getState()); } #[Test] public function it_allows_accepting_multiple_subscription(): void { $this->markAsSkippedIfNoStateMachineIsAvailable(); DefaultSubscriptionsStory::load(); $martyMcFly = SubscriptionFactory::find(['email' => 'marty.mcfly@bttf.com']); $docBrown = SubscriptionFactory::find(['email' => 'doc.brown@bttf.com']); $this->client->request('GET', '/admin/subscriptions?limit=10'); $this->client->submitForm('Bulk accept'); $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); $martyMcFly = refresh($martyMcFly); $this->assertSame('accepted', $martyMcFly->getState()); $docBrown = refresh($docBrown); $this->assertSame('accepted', $docBrown->getState()); } private function markAsSkippedIfNoStateMachineIsAvailable(): void { if (!class_exists(winzouStateMachineBundle::class) && !class_exists(Registry::class)) { $this->markTestSkipped('No State machine is available.'); } } } ================================================ FILE: tests/Application/src/Tests/Controller/SubscriptionXmlApiTest.php ================================================ 'application/xml', 'HTTP_ACCEPT' => 'application/xml', ]; #[Test] public function it_allows_showing_a_subscription(): void { $subscription = SubscriptionFactory::new() ->withEmail('marty.mcfly@bttf.com') ->create() ; $this->client->request('GET', '/ajax/subscriptions/' . $subscription->getId(), server: self::$headers); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'text/xml; charset=utf-8'); $this->assertResponseMatchesPattern( <<<'XML' new marty.mcfly@bttf.com XML ); } #[Test] public function it_allows_indexing_subscriptions(): void { DefaultSubscriptionsStory::load(); $this->client->request('GET', '/ajax/subscriptions', server: self::$headers); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'text/xml; charset=utf-8'); $this->assertResponseMatchesPattern( <<<'XML' new marty.mcfly@bttf.com new doc.brown@bttf.com accepted biff.tannen@bttf.com new lorraine.baines@bttf.com new george.mcfly@bttf.com new jennifer.parker@bttf.com 1 0 0 10 6 1 XML ); } #[Test] public function it_allows_creating_a_subscription(): void { $data = <<<'XML' marty.mcfly@bttf.com XML ; $this->client->request(method: 'POST', uri: '/ajax/subscriptions', server: self::$headers, content: $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); $this->assertResponseHeaderSame('content-type', 'text/xml; charset=utf-8'); $this->assertResponseMatchesPattern( <<<'XML' new marty.mcfly@bttf.com XML ); } #[Test] public function it_does_not_allow_to_create_a_subscription_if_there_is_a_validation_error(): void { $data = <<<'XML' XML ; $this->client->request(method: 'POST', uri: '/ajax/subscriptions', server: self::$headers, content: $data); $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); $this->assertResponseHeaderSame('content-type', 'text/xml; charset=utf-8'); if (Kernel::VERSION_ID < 60400) { $this->assertResponseMatchesPattern( <<<'XML' https://symfony.com/errors/validation Validation Failed email: This value should not be blank. email This value should not be blank. "" urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3 XML ); return; } $this->assertResponseMatchesPattern( <<<'XML' https://symfony.com/errors/validation Validation Failed email: This value should not be blank. email This value should not be blank. "" urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3 XML ); } #[Test] public function it_allows_updating_a_subscription(): void { $subscription = SubscriptionFactory::createOne(); $data = <<<'XML' calvin.klein@bttf.com XML ; $this->client->request(method: 'PUT', uri: '/ajax/subscriptions/' . $subscription->getId(), server: self::$headers, content: $data); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT); } #[Test] public function it_does_not_allow_to_update_a_subscription_if_there_is_a_validation_error(): void { $subscription = SubscriptionFactory::createOne(); $data = <<<'XML' XML ; $this->client->request(method: 'PUT', uri: '/ajax/subscriptions/' . $subscription->getId(), server: self::$headers, content: $data); $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); $this->assertResponseHeaderSame('content-type', 'text/xml; charset=utf-8'); if (Kernel::VERSION_ID < 60400) { $this->assertResponseMatchesPattern( <<<'XML' https://symfony.com/errors/validation Validation Failed email: This value should not be blank. email This value should not be blank. "" urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3 XML ); return; } $this->assertResponseMatchesPattern( <<<'XML' https://symfony.com/errors/validation Validation Failed email: This value should not be blank. email This value should not be blank. "" urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3 XML ); } #[Test] public function it_allows_removing_a_subscription(): void { $subscription = SubscriptionFactory::createOne(); $this->client->request(method: 'DELETE', uri: '/ajax/subscriptions/' . $subscription->getId(), server: self::$headers); $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT); } } ================================================ FILE: tests/Application/src/Tests/Validator/TranslatableValidatorTest.php ================================================ getBookBy(); $book->getTranslation('pl_PL')->setLocale(''); $errors = $this->getValidator()->validate($book, null, ['sylius']); $this->assertCount(1, $errors); $this->assertSame('sylius.resource.translation.locale.not_blank', $errors->get(0)->getMessageTemplate()); } /** @test */ public function it_fails_validation_with_invalid_locale(): void { DefaultBooksStory::load(); $book = $this->getBookBy(); $book->getTranslation('pl_PL')->setLocale('invalid'); $errors = $this->getValidator()->validate($book, null, ['sylius']); $this->assertCount(1, $errors); $this->assertSame('sylius.resource.translation.locale.invalid', $errors->get(0)->getMessageTemplate()); } /** @test */ public function it_fails_validation_with_not_unique_locale(): void { DefaultBooksStory::load(); $book = $this->getBookBy(); $book->getTranslation('pl_PL')->setLocale('en_US'); $errors = $this->getValidator()->validate($book, null, ['sylius']); $this->assertCount(1, $errors); $this->assertSame('sylius.resource.translation.locale.unique', $errors->get(0)->getMessageTemplate()); } private function getValidator(): ValidatorInterface { return self::getContainer()->get(ValidatorInterface::class); } private function getBookBy(array $criteria = ['author' => 'J.R.R. Tolkien']): ?Book { return self::getContainer()->get('app.repository.book')->findOneBy($criteria); } } ================================================ FILE: tests/Application/templates/ScienceBook/create.html.twig ================================================

New Science Book

{{ form_start(form) }} {{ form_widget(form) }}
{{ form_end(form) }} ================================================ FILE: tests/Application/templates/ScienceBook/index.html.twig ================================================

Books

{% for book in resources.data %} {% endfor %}
ID Title Author Actions
{{ book.id }}{{ book.title }}{{ book.authorFirstName }} {{ book.authorLastName }}
================================================ FILE: tests/Application/templates/ScienceBook/show.html.twig ================================================

Science book

ID: {{ resource.id }}

Title: {{ resource.title }} Author: {{ resource.authorFirstName }} {{ resource.authorLastName }} ================================================ FILE: tests/Application/templates/ScienceBook/update.html.twig ================================================

Edit Science Book

{{ form_start(form) }} {{ form_widget(form) }}
{{ form_end(form) }} ================================================ FILE: tests/Application/templates/board_game/show.html.twig ================================================

Science book

ID: {{ resource.id }}

Name: {{ resource.name }} ================================================ FILE: tests/Application/templates/crud/create.html.twig ================================================ {% extends 'layout.html.twig' %} {% block body %}

{{ (operation.resource.applicationName ~ '.ui.new_' ~ operation.resource.name)|trans }}

{{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }} {{ form_widget(form) }}
{{ form_end(form) }} {% endblock %} ================================================ FILE: tests/Application/templates/crud/index.html.twig ================================================ {% extends 'layout.html.twig' %} {% block body %}

{{ (operation.resource.applicationName ~ '.ui.' ~ operation.resource.pluralName)|trans }}

{% set grid = resources %} {% set definition = grid.definition|default(null) %} {% if definition.actionGroups.bulk is defined and definition.getEnabledActions('bulk')|length > 0 %} {% for action in definition.getEnabledActions('bulk') %} {{ sylius_grid_render_bulk_action(grid, action, null) }} {% endfor %} {% endif %} {% if not definition %} This template doesn't support non-grid data. {% endif %} {% if definition %} {% for field in definition.fields %} {% if field.enabled %} {% endif %} {% if definition.actionGroups.item is defined and definition.getEnabledActions('item')|length > 0 %} {% endif %} {% endfor %} {% for resource in resources.data %} {% for field in definition.enabledFields %} {% if definition.actionGroups.item is defined and definition.getEnabledActions('item')|length > 0 %} {% endif %} {% endfor %} {% endfor %}
{{ field.label|trans }}Actions
{{ sylius_grid_render_field(grid, field, resource) }} {% for action in definition.getEnabledActions('item') %} {{ sylius_grid_render_action(grid, action, resource) }} {% endfor %}
{% endif %} {% endblock %} ================================================ FILE: tests/Application/templates/crud/update.html.twig ================================================ {% extends 'layout.html.twig' %} {% block body %}

{{ (operation.resource.applicationName ~ '.ui.edit_' ~ operation.resource.name)|trans }}

{{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }} {{ form_widget(form) }}
{{ form_end(form) }} {% endblock %} ================================================ FILE: tests/Application/templates/grid/action/apply_transition.html.twig ================================================ {% set labeled = options.labeled is defined ? options.labeled : true %} {% if sylius_state_machine_can(data, options.transition, options.graph) %}
{% endif %} ================================================ FILE: tests/Application/templates/grid/action/delete.html.twig ================================================ {% set path = options.link.url|default(path(options.link.route|default(grid.requestConfiguration.getRouteName('delete')), options.link.parameters|default({'id': data.id}))) %}
================================================ FILE: tests/Application/templates/grid/action/show.html.twig ================================================ {% set path = options.link.url|default(path(options.link.route|default(grid.requestConfiguration.getRouteName('show')), options.link.parameters|default({'id': data.id}))) %} Show ================================================ FILE: tests/Application/templates/grid/action/update.html.twig ================================================ {% set path = options.link.url|default(path(options.link.route|default(grid.requestConfiguration.getRouteName('update')), options.link.parameters|default({'id': data.id}))) %} Edit ================================================ FILE: tests/Application/templates/grid/bulk_action/apply_transition.html.twig ================================================ {% set labeled = true %} {% set message = action.label %} {% set path = options.link.url|default(path(options.link.route|default(grid.requestConfiguration.getRouteName('bulk_update')), options.link.parameters|default({}))) %}
{% for resource in grid.data %} {% endfor %}
================================================ FILE: tests/Application/templates/grid/bulk_action/delete.html.twig ================================================ {% set path = options.link.url|default(path(options.link.route|default(grid.requestConfiguration.getRouteName('bulk_delete')), options.link.parameters|default({}))) %}
{% for resource in grid.data %} {% endfor %}
================================================ FILE: tests/Application/templates/layout/_flashes.html.twig ================================================ {% if app.session is not null %} {# Enforce starting session #} {% set token = app.token %} {% endif %} {% if app.session is not null and app.session.started %} {% for type in ['success', 'error', 'info', 'warning'] %} {% for flash in app.session.flashbag.get(type) %} {% endfor %} {% endfor %} {% endif %} ================================================ FILE: tests/Application/templates/layout.html.twig ================================================ {% block title %}Sylius{% endblock %} {% include 'layout/_flashes.html.twig' %} {% block body %} {% endblock %} ================================================ FILE: tests/Application/templates/subscription/show.html.twig ================================================ {% extends 'layout.html.twig' %} {% block body %}

Subscription

ID: {{ resource.id }}

  • Email: {{ resource.email }}
  • Foo: {{ foo }}
{% endblock %} ================================================ FILE: tests/Application/translations/messages.en.yaml ================================================ app: ui: board_games: Board games edit_board_game: Edit board game new_board_game: New board game new_subscription: New subscription speakers: Speakers ================================================ FILE: tests/Bundle/Command/DebugResourceCommandTest.php ================================================ registry = $this->createMock(RegistryInterface::class); $this->resourceCollectionMetadataFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); $command = new DebugResourceCommand($this->registry, $this->resourceCollectionMetadataFactory); $this->tester = new CommandTester($command); } /** * @test */ public function it_lists_all_resources_if_no_argument_is_given(): void { $this->registry->method('getAll')->willReturn([$this->createMetadata('one'), $this->createMetadata('two')]); $this->tester->execute([]); $display = $this->tester->getDisplay(); $this->assertEquals( <<registry->method('get')->with('metadata.one')->willReturn($this->createMetadata('one')); $resourceMetadataCollection = new ResourceMetadataCollection(); $this->resourceCollectionMetadataFactory->method('create')->with('App\One')->willReturn($resourceMetadataCollection); $this->tester->execute([ 'resource' => 'metadata.one', ]); $display = $this->tester->getDisplay(); $this->assertStringContainsString('[INFO] This resource has no defined operations.', $display); } /** * @test */ public function it_displays_the_metadata_for_given_resource_alias_with_operations(): void { $this->registry->method('get')->with('metadata.one')->willReturn($this->createMetadata('one')); $resourceMetadata = (new ResourceMetadata())->withOperations(new Operations([ 'app_one_index' => new Index(name: 'app_one_index', provider: 'App\GetOneItemProvider'), 'app_one_create' => new Create(name: 'app_one_create', processor: 'App\CreateOneProcessor'), ])); $resourceMetadataCollection = new ResourceMetadataCollection([$resourceMetadata]); $this->resourceCollectionMetadataFactory->method('create')->with('App\One')->willReturn($resourceMetadataCollection); $this->tester->execute([ 'resource' => 'metadata.one', ]); $display = $this->tester->getDisplay(); $this->assertEquals( <<registry->method('getByClass')->with('App\Resource')->willReturn($this->createMetadata('one')); $resourceMetadata = (new ResourceMetadata(alias: 'sylius.one'))->withOperations(new Operations([ 'app_one_index' => new Index(name: 'app_one_index', provider: 'App\GetOneItemProvider'), 'app_one_create' => new Create(name: 'app_one_create', processor: 'App\CreateOneProcessor'), ])); $resourceMetadataCollection = new ResourceMetadataCollection([$resourceMetadata]); $this->resourceCollectionMetadataFactory->method('create')->with('App\One')->willReturn($resourceMetadataCollection); $this->tester->execute([ 'resource' => 'App\Resource', ]); $display = $this->tester->getDisplay(); $this->assertEquals( <<registry->method('get')->with('metadata.one')->willReturn($this->createMetadata('one')); $resourceMetadata = (new ResourceMetadata(alias: 'sylius.one'))->withOperations(new Operations([ 'app_one_index' => new Index(name: 'app_one_index', provider: 'App\GetOneItemProvider'), 'app_one_create' => new Create( template: 'register.html.twig', name: 'app_one_create', provider: 'App\ItemProvider', processor: 'App\CreateOneProcessor', responder: 'App\ItemResponder', factory: 'App\CreateOneFactory', factoryMethod: 'createWithCreator', factoryArguments: ['creator' => 'user'], eventShortName: 'register', vars: ['foo' => 'bar'], ), ])); $resourceMetadataCollection = new ResourceMetadataCollection([$resourceMetadata]); $this->resourceCollectionMetadataFactory->method('create')->with('App\One')->willReturn($resourceMetadataCollection); $this->tester->execute([ 'resource' => 'metadata.one', 'operation' => 'app_one_create', ]); $display = $this->tester->getDisplay(); $this->assertEquals( << "user" ] stateMachineComponent null stateMachineTransition null stateMachineGraph null twigContextFactory null methods [ "GET", "POST" ] path null routeName null routePrefix null routeRequirements null routeCondition null routePriority null redirectTo null redirectToRoute null redirectArguments null vars [ "foo" => "bar" ] provider "App\ItemProvider" processor "App\CreateOneProcessor" responder "App\ItemResponder" repository null template "register.html.twig" shortName "create" name "app_one_create" repositoryMethod null repositoryArguments null read null write null validate null deserialize null serialize null formType null formOptions null normalizationContext null denormalizationContext null validationContext null eventShortName "register" notificationMessage null security null securityMessage null ------------------------ -------------------------- TXT , $display, ); } #[Test] public function it_displays_the_legacy_resource_metadata_for_given_resource_alias(): void { $this->registry->method('get')->with('metadata.one')->willReturn($this->createMetadata('one')); $resourceMetadata = (new ResourceMetadata(alias: 'sylius.one')); $resourceMetadataCollection = new ResourceMetadataCollection([$resourceMetadata]); $this->resourceCollectionMetadataFactory->method('create')->with('App\One')->willReturn($resourceMetadataCollection); $this->tester->execute([ 'resource' => 'metadata.one', '--legacy' => true, ]); $display = $this->tester->getDisplay(); $this->assertEquals( << "App\One", "foo" => "bar", "bar" => "foo" ] whatever [ "something" => [ "elephants" => "camels" ] ] ----------------------- ----------------------------- TXT , $display, ); } private function createMetadata(string $suffix): MetadataInterface { return Metadata::fromAliasAndConfiguration(sprintf('sylius.%s', $suffix), [ 'driver' => 'doctrine/foobar', 'classes' => [ 'model' => 'App\\' . ucfirst($suffix), 'foo' => 'bar', 'bar' => 'foo', ], 'whatever' => [ 'something' => [ 'elephants' => 'camels', ], ], ]); } } ================================================ FILE: tests/Bundle/Configuration/ConfigurationTest.php ================================================ assertConfigurationIsValid( [ [], ], ); } /** @test */ public function it_has_no_default_drivers(): void { $this->assertProcessedConfigurationEquals( [], [ 'drivers' => [], ], 'drivers', ); } /** @test */ public function its_drivers_can_be_customized(): void { $this->assertProcessedConfigurationEquals( [ ['drivers' => [ 'doctrine/orm', ]], ], [ 'drivers' => [ 'doctrine/orm', ], ], 'drivers', ); } /** @test */ public function it_has_no_default_mapping_paths(): void { $this->assertProcessedConfigurationEquals( [ [], ], [ 'mapping' => [ 'imports' => [], 'paths' => [], ], ], 'mapping', ); } /** @test */ public function its_mapping_paths_can_be_customized(): void { $this->assertProcessedConfigurationEquals( [ ['mapping' => [ 'paths' => ['path/to/resources'], ]], ], [ 'mapping' => [ 'imports' => [], 'paths' => [ 'path/to/resources', ], ], ], 'mapping', ); } /** @test */ public function its_default_templates_dir_can_be_customized(): void { $this->assertProcessedConfigurationEquals( [ ['settings' => [ 'default_templates_dir' => 'path/to/templates', ]], ], [ 'settings' => [ 'default_templates_dir' => 'path/to/templates', ], ], 'settings.default_templates_dir', ); } /** @test */ public function it_has_default_authorization_checker(): void { $this->assertProcessedConfigurationEquals( [ [], ], ['authorization_checker' => 'sylius.resource_controller.authorization_checker.disabled'], 'authorization_checker', ); } /** @test */ public function its_authorization_checker_can_be_customized(): void { $this->assertProcessedConfigurationEquals( [ ['authorization_checker' => 'custom_service'], ], ['authorization_checker' => 'custom_service'], 'authorization_checker', ); } /** @test */ public function its_authorization_checker_cannot_be_empty(): void { $this->assertPartialConfigurationIsInvalid( [ ['authorization_checker' => ''], ], 'authorization_checker', ); } protected function getConfiguration(): ConfigurationInterface { return new Configuration(); } } ================================================ FILE: tests/Bundle/Context/Initiator/LegacyRequestContextInitiatorTest.php ================================================ resourceRegistryMock = $this->createMock(RegistryInterface::class); $this->requestConfigurationFactoryMock = $this->createMock(RequestConfigurationFactoryInterface::class); $this->decoratedMock = $this->createMock(RequestContextInitiatorInterface::class); $this->legacyRequestContextInitiator = new LegacyRequestContextInitiator( $this->resourceRegistryMock, $this->requestConfigurationFactoryMock, $this->decoratedMock, ); $this->requestMock = $this->createMock(Request::class); $this->metadataMock = $this->createMock(MetadataInterface::class); $this->requestConfigurationMock = $this->createMock(RequestConfiguration::class); } public function testAddsMetadataAndRequestConfigurationToTheContext(): void { $parameterBag = new ParameterBag(['_sylius' => ['resource' => 'app.dummy']]); $this->requestConfigurationMock->expects($this->once())->method('getParameters')->willReturn($parameterBag); $this->requestConfigurationMock->expects($this->once())->method('getVars')->willReturn([]); $this->requestMock->attributes = $parameterBag; $this->decoratedMock ->expects($this->once()) ->method('initializeContext') ->with($this->requestMock) ->willReturn(new Context()) ; $this->resourceRegistryMock ->expects($this->once()) ->method('get') ->with('app.dummy') ->willReturn($this->metadataMock) ; $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $this->requestMock) ->willReturn($this->requestConfigurationMock) ; $result = $this->legacyRequestContextInitiator->initializeContext($this->requestMock); $this->assertInstanceOf(Context::class, $result); $this->assertSame($this->metadataMock, $result->get(MetadataOption::class)?->metadata()); $this->assertSame( $this->requestConfigurationMock, $result->get(RequestConfigurationOption::class)?->requestConfiguration(), ); } public function testDirectlyReturnsTheContextWhenRequestHasNoSyliusAttributes(): void { $this->requestMock->attributes = new ParameterBag(); $this->decoratedMock ->expects($this->once()) ->method('initializeContext') ->with($this->requestMock) ->willReturn(new Context()) ; $this->resourceRegistryMock ->expects($this->never()) ->method('get') ->with('app.dummy') ->willReturn($this->metadataMock) ; $this->requestConfigurationFactoryMock ->expects($this->never()) ->method('create') ->with($this->metadataMock, $this->requestMock) ->willReturn($this->requestConfigurationMock) ; $result = $this->legacyRequestContextInitiator->initializeContext($this->requestMock); $this->assertInstanceOf(Context::class, $result); $this->assertNull($result->get(MetadataOption::class)); $this->assertNull($result->get(RequestConfigurationOption::class)); } public function testDirectlyReturnsTheContextWhenRequestHasNoResourceOnAttributes(): void { $this->requestMock->attributes = new ParameterBag(['_sylius' => ['section' => 'admin']]); $this->decoratedMock ->expects($this->once()) ->method('initializeContext') ->with($this->requestMock) ->willReturn(new Context()) ; $this->resourceRegistryMock ->expects($this->never()) ->method('get') ->with('app.dummy') ->willReturn($this->metadataMock) ; $this->requestConfigurationFactoryMock ->expects($this->never()) ->method('create') ->with($this->metadataMock, $this->requestMock) ->willReturn($this->requestConfigurationMock) ; $result = $this->legacyRequestContextInitiator->initializeContext($this->requestMock); $this->assertInstanceOf(Context::class, $result); $this->assertNull($result->get(MetadataOption::class)); $this->assertNull($result->get(RequestConfigurationOption::class)); } } ================================================ FILE: tests/Bundle/Context/Option/RequestConfigurationOptionTest.php ================================================ requestConfigurationMock = $this->createMock(RequestConfiguration::class); $this->requestConfigurationOption = new RequestConfigurationOption($this->requestConfigurationMock); } public function testReturnsRequestConfiguration(): void { $this->assertSame($this->requestConfigurationMock, $this->requestConfigurationOption->requestConfiguration()); } } ================================================ FILE: tests/Bundle/Controller/DisabledAuthorizationCheckerTest.php ================================================ disabledAuthorizationChecker = new DisabledAuthorizationChecker(); } public function testImplementsResourceControllerAuthorizationCheckerInterface(): void { $this->assertInstanceOf(AuthorizationCheckerInterface::class, $this->disabledAuthorizationChecker); } public function testAlwaysReturnsTrue(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); $this->assertTrue($this->disabledAuthorizationChecker->isGranted($requestConfigurationMock, 'create')); $this->assertTrue($this->disabledAuthorizationChecker->isGranted($requestConfigurationMock, 'update')); $this->assertTrue($this->disabledAuthorizationChecker->isGranted($requestConfigurationMock, 'custom')); } } ================================================ FILE: tests/Bundle/Controller/EventDispatcherTest.php ================================================ eventDispatcherMock = $this->createMock(EventDispatcherInterface::class); $this->eventDispatcher = new EventDispatcher($this->eventDispatcherMock); } public function testImplementsEventDispatcherInterface(): void { $this->assertInstanceOf(ControllerEventDispatcherInterface::class, $this->eventDispatcher); } public function testDispatchesAppropriateEventForAResource(): void { $requestConfiguration = $this->createRequestConfiguration(null); $resource = $this->createMock(ResourceInterface::class); $this->expectEventDispatched('sylius.product.show'); $result = $this->eventDispatcher->dispatch(ResourceActions::SHOW, $requestConfiguration, $resource); $this->assertInstanceOf(ResourceControllerEvent::class, $result); } public function testDispatchesAppropriateCustomEventForAResource(): void { $requestConfiguration = $this->createRequestConfiguration('register'); $resource = $this->createMock(ResourceInterface::class); $this->expectEventDispatched('sylius.product.register'); $result = $this->eventDispatcher->dispatch(ResourceActions::CREATE, $requestConfiguration, $resource); $this->assertInstanceOf(ResourceControllerEvent::class, $result); } public function testDispatchesEventForACollectionOfResources(): void { $requestConfiguration = $this->createRequestConfiguration('register'); $resources = $this->createMock(Collection::class); $this->expectEventDispatched('sylius.product.register'); $result = $this->eventDispatcher->dispatchMultiple(ResourceActions::CREATE, $requestConfiguration, $resources); $this->assertInstanceOf(ResourceControllerEvent::class, $result); } public function testDispatchesAppropriatePreEventForAResource(): void { $requestConfiguration = $this->createRequestConfiguration(null); $resource = $this->createMock(ResourceInterface::class); $this->expectEventDispatched('sylius.product.pre_create'); $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $requestConfiguration, $resource); } public function testDispatchesAppropriateCustomPreEventForAResource(): void { $requestConfiguration = $this->createRequestConfiguration('register'); $resource = $this->createMock(ResourceInterface::class); $this->expectEventDispatched('sylius.product.pre_register'); $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $requestConfiguration, $resource); } public function testDispatchesAppropriatePostEventForAResource(): void { $requestConfiguration = $this->createRequestConfiguration(null); $resource = $this->createMock(ResourceInterface::class); $this->expectEventDispatched('sylius.product.post_create'); $this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $requestConfiguration, $resource); } public function testDispatchesAppropriateCustomPostEventForAResource(): void { $requestConfiguration = $this->createRequestConfiguration('register'); $resource = $this->createMock(ResourceInterface::class); $this->expectEventDispatched('sylius.product.post_register'); $result = $this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $requestConfiguration, $resource); $this->assertInstanceOf(ResourceControllerEvent::class, $result); } /** * @return MockObject&RequestConfiguration */ private function createRequestConfiguration(?string $customEvent): MockObject { /** @var MockObject&MetadataInterface $metadata */ $metadata = $this->createMock(MetadataInterface::class); $metadata->method('getApplicationName')->willReturn('sylius'); $metadata->method('getName')->willReturn('product'); /** @var MockObject&RequestConfiguration $requestConfiguration */ $requestConfiguration = $this->createMock(RequestConfiguration::class); $requestConfiguration->method('getEvent')->willReturn($customEvent); $requestConfiguration->method('getMetadata')->willReturn($metadata); return $requestConfiguration; } private function expectEventDispatched(string $eventName): void { $this->eventDispatcherMock ->expects($this->once()) ->method('dispatch') ->with( $this->isInstanceOf(ResourceControllerEvent::class), $eventName, ); } } ================================================ FILE: tests/Bundle/Controller/FlashHelperTest.php ================================================ session = new Session(new MockArraySessionStorage()); $this->session->registerBag(new FlashBag()); $requestStack = new RequestStack(); $request = new Request(); $request->setSession($this->session); $requestStack->push($request); $this->translator = $this->createMock(TranslatorInterface::class); $this->flashHelper = new FlashHelper($requestStack, $this->translator, 'en_US'); } public function testAddsSuccessFlashWithTranslatedResourceNameForSingleResource(): void { $this->configureTranslator(['app.ui.product' => 'Product']); $requestConfiguration = $this->createRequestConfiguration('product', 'sylius.resource.create'); $this->flashHelper->addSuccessFlash($requestConfiguration, 'create'); $this->assertFlashMessage('success', 'sylius.resource.create', ['%resource%' => 'Product']); } public function testAddsSuccessFlashWithTranslatedPluralResourceNameForBulkAction(): void { $this->configureTranslator(['app.ui.products' => 'Products']); $requestConfiguration = $this->createRequestConfiguration('product', 'sylius.resource.bulk_delete', 'products'); $this->flashHelper->addSuccessFlash($requestConfiguration, 'bulk_delete'); $this->assertFlashMessage('success', 'sylius.resource.bulk_delete', ['%resources%' => 'Products']); } public function testConvertsCamelCaseToSnakeCaseForResourceNames(): void { $this->configureTranslator(['app.ui.product_variant' => 'Product Variant']); $requestConfiguration = $this->createRequestConfiguration('productVariant', 'sylius.resource.update'); $this->flashHelper->addSuccessFlash($requestConfiguration, 'update'); $this->assertFlashMessage('success', 'sylius.resource.update', ['%resource%' => 'Product Variant']); } public function testConvertsCamelCaseToSnakeCaseForPluralResourceNames(): void { $this->configureTranslator(['app.ui.product_variants' => 'Product Variants']); $requestConfiguration = $this->createRequestConfiguration('productVariant', 'sylius.resource.bulk_update', 'productVariants'); $this->flashHelper->addSuccessFlash($requestConfiguration, 'bulk_update'); $this->assertFlashMessage('success', 'sylius.resource.bulk_update', ['%resources%' => 'Product Variants']); } public function testAddsErrorFlashWithTranslatedResourceName(): void { $this->configureTranslator(['app.ui.customer' => 'Customer']); $requestConfiguration = $this->createRequestConfiguration('customer', 'sylius.resource.delete'); $this->flashHelper->addErrorFlash($requestConfiguration, 'delete'); $this->assertFlashMessage('error', 'sylius.resource.delete', ['%resource%' => 'Customer']); } public function testDoesNotAddFlashWhenMessageIsEmpty(): void { $this->configureTranslator(['sylius.ui.product' => 'Product']); $requestConfiguration = $this->createRequestConfiguration('product', ''); $this->flashHelper->addSuccessFlash($requestConfiguration, 'create'); $this->assertFlashCount('success', 0); } public function testTranslatesResourceNameToGerman(): void { $this->configureTranslator(['app.ui.product' => 'Produkt']); $requestConfiguration = $this->createRequestConfiguration('product', 'sylius.resource.create'); $this->flashHelper->addSuccessFlash($requestConfiguration, 'create'); $this->assertFlashMessage('success', 'sylius.resource.create', ['%resource%' => 'Produkt']); } public function testTranslatesPluralResourceNameToGerman(): void { $this->configureTranslator(['app.ui.products' => 'Produkte']); $requestConfiguration = $this->createRequestConfiguration('product', 'sylius.resource.bulk_delete', 'products'); $this->flashHelper->addSuccessFlash($requestConfiguration, 'bulk_delete'); $this->assertFlashMessage('success', 'sylius.resource.bulk_delete', ['%resources%' => 'Produkte']); } public function testTranslatesResourceNameToGermanForUpdate(): void { $this->configureTranslator(['app.ui.customer' => 'Kunde']); $requestConfiguration = $this->createRequestConfiguration('customer', 'sylius.resource.update'); $this->flashHelper->addSuccessFlash($requestConfiguration, 'update'); $this->assertFlashMessage('success', 'sylius.resource.update', ['%resource%' => 'Kunde']); } private function createRequestConfiguration(string $resourceName, string $flashMessage, ?string $pluralName = null, ?string $applicationName = null): RequestConfiguration { $metadata = $this->createMock(MetadataInterface::class); $metadata->method('getName')->willReturn($resourceName); $metadata->method('getApplicationName')->willReturn($applicationName ?? 'app'); if ($pluralName !== null) { $metadata->method('getPluralName')->willReturn($pluralName); } $requestConfiguration = $this->createMock(RequestConfiguration::class); $requestConfiguration->method('getMetadata')->willReturn($metadata); $requestConfiguration->method('getFlashMessage')->willReturn($flashMessage); return $requestConfiguration; } /** * @param array $translations */ private function configureTranslator(array $translations): void { $this->translator ->method('trans') ->willReturnCallback(function (string $id, array $parameters, string $domain) use ($translations) { if ($domain === 'messages' && isset($translations[$id])) { return $translations[$id]; } return $id; }); } /** * @param array $expectedParameters */ private function assertFlashMessage(string $type, string $expectedMessage, array $expectedParameters): void { $flashes = $this->session->getFlashBag()->get($type); $this->assertCount(1, $flashes); $this->assertSame($expectedMessage, $flashes[0]['message']); $this->assertSame($expectedParameters, $flashes[0]['parameters']); } private function assertFlashCount(string $type, int $expectedCount): void { $flashes = $this->session->getFlashBag()->get($type); $this->assertCount($expectedCount, $flashes); } } ================================================ FILE: tests/Bundle/Controller/NewResourceFactoryTest.php ================================================ newResourceFactory = new NewResourceFactory(); } public function testImplementsNewResourceFactoryInterface(): void { $this->assertInstanceOf(NewResourceFactoryInterface::class, $this->newResourceFactory); } public function testCallsCreateNewByDefaultIfNoCustomMethodConfigured(): void { $requestConfiguration = $this->createRequestConfiguration(null, []); $factory = $this->createMock(FactoryInterface::class); $resource = $this->createMock(ResourceInterface::class); $factory ->expects($this->once()) ->method('createNew') ->willReturn($resource); $result = $this->newResourceFactory->create($requestConfiguration, $factory); $this->assertSame($resource, $result); } public function testCallsProperFactoryMethodsBasedOnConfiguration(): void { $requestConfiguration = $this->createRequestConfiguration('createNew', ['00032']); $factory = $this->createMock(FactoryInterface::class); $resource = $this->createMock(ResourceInterface::class); $factory ->expects($this->once()) ->method('createNew') ->with('00032') ->willReturn($resource); $result = $this->newResourceFactory->create($requestConfiguration, $factory); $this->assertSame($resource, $result); } public function testCallsProperServiceBasedOnConfiguration(): void { $customFactory = $this->createMock(FactoryInterface::class); $requestConfiguration = $this->createRequestConfiguration([$customFactory, 'createNew'], ['foo', 'bar']); $factory = $this->createMock(FactoryInterface::class); $resource = $this->createMock(ResourceInterface::class); $customFactory ->expects($this->once()) ->method('createNew') ->with('foo', 'bar') ->willReturn($resource); $factory ->expects($this->never()) ->method('createNew'); $result = $this->newResourceFactory->create($requestConfiguration, $factory); $this->assertSame($resource, $result); } /** * @param string|array|null $factoryMethod * @param array $factoryArguments * * @return MockObject&RequestConfiguration */ private function createRequestConfiguration(string|array|null $factoryMethod, array $factoryArguments): MockObject { /** @var MockObject&RequestConfiguration $requestConfiguration */ $requestConfiguration = $this->createMock(RequestConfiguration::class); $requestConfiguration->method('getFactoryMethod')->willReturn($factoryMethod); $requestConfiguration->method('getFactoryArguments')->willReturn($factoryArguments); return $requestConfiguration; } } ================================================ FILE: tests/Bundle/Controller/ParametersParserTest.php ================================================ parametersParser = new ParametersParser(new Container(), new ExpressionLanguage()); } public function testImplementsParametersParserInterface(): void { $this->assertInstanceOf(ParametersParserInterface::class, $this->parametersParser); } public function testParsesStringParameters(): void { $request = new Request(); $request->request->set('string', 'Lorem ipsum'); $this->assertSame(['nested' => ['string' => 'Lorem ipsum']], $this->parametersParser ->parseRequestValues(['nested' => ['string' => '$string']], $request)) ; } public function testParsesBooleanParameters(): void { $request = new Request(); $request->request->set('boolean', true); $this->assertSame(['nested' => ['boolean' => true]], $this->parametersParser ->parseRequestValues(['nested' => ['boolean' => '$boolean']], $request)) ; } public function testParsesArrayParameters(): void { $request = new Request(); $request->request->set('array', ['foo' => 'bar']); $this->assertSame(['nested' => ['array' => ['foo' => 'bar']]], $this->parametersParser ->parseRequestValues(['nested' => ['array' => '$array']], $request)) ; } public function testParsesStringParameterAndCastsItIntoInt(): void { $request = new Request(); $request->request->set('int', '5'); $this->assertSame(['nested' => ['int' => 5]], $this->parametersParser ->parseRequestValues(['nested' => ['int' => '!!int $int']], $request)) ; } public function testParsesStringParameterAndCastsItIntoFloat(): void { $request = new Request(); $request->request->set('float', '5.4'); $this->assertSame(['nested' => ['float' => 5.4]], $this->parametersParser ->parseRequestValues(['nested' => ['float' => '!!float $float']], $request)) ; } public function testThrowsExceptionIfStringParameterIsGoingToBeCastedIntoInvalidType(): void { $request = new Request(); $request->request->set('int', 5); $this->expectException(\InvalidArgumentException::class); $this->parametersParser->parseRequestValues(['nested' => ['int' => '!!invalid $int']], $request); } public function testThrowsExceptionIfInvalidTypecastIsProvided(): void { $request = new Request(); $request->request->set('int', 5); $this->expectException(\InvalidArgumentException::class); $this->parametersParser->parseRequestValues(['nested' => ['int' => '!!int!! $int']], $request); } public function testParsesStringParameterAndCastsItIntoBool(): void { $request = new Request(); $request->request->set('bool0', '0'); $request->request->set('bool1', '1'); $this->assertSame(['nested' => ['bool' => false]], $this->parametersParser ->parseRequestValues(['nested' => ['bool' => '!!bool $bool0']], $request)) ; $this->assertSame(['nested' => ['bool' => true]], $this->parametersParser ->parseRequestValues(['nested' => ['bool' => '!!bool $bool1']], $request)) ; } public function testParsesAnExpressionAndCastsItIntoAGivenType(): void { $request = new Request(); $this->assertSame(['nested' => ['cast' => 5]], $this->parametersParser ->parseRequestValues(['nested' => ['cast' => '!!int expr:"5"']], $request)) ; } public function testParsesAnExpressionWithSpacesAndCastsItIntoAGivenType(): void { $request = new Request(); $this->assertSame(['nested' => ['cast' => 10]], $this->parametersParser ->parseRequestValues(['nested' => ['cast' => '!!int expr:"5" + "5"']], $request)) ; } public function testParsesExpressions(): void { $request = new Request(); $this->assertSame(['nested' => ['boolean' => true]], $this->parametersParser ->parseRequestValues(['nested' => ['boolean' => 'expr:"foo" in ["foo", "bar"]']], $request)) ; } public function testParsesExpressionsWithStringParameters(): void { $request = new Request(); $request->request->set('string', 'lorem ipsum'); $this->assertSame(['expression' => true], $this->parametersParser ->parseRequestValues(['expression' => 'expr:$string === "lorem ipsum"'], $request)) ; } public function testParsesExpressionsWithScalarParameters(): void { $request = new Request(); $request->request->set('number', 6); $this->assertSame(['expression' => true], $this->parametersParser ->parseRequestValues(['expression' => 'expr:$number === 6'], $request)) ; } public function testThrowsAnExceptionIfArrayParameterIsInjectedIntoExpression(): void { $request = new Request(); $request->request->set('array', ['foo', 'bar']); $this->expectException(\InvalidArgumentException::class); $this->parametersParser->parseRequestValues(['expression' => 'expr:"foo" in $array'], $request); } public function testThrowsAnExceptionIfObjectParameterIsInjectedIntoExpression(): void { /** @var array|bool|float|int|string|null $objectMock */ $objectMock = $this->createMock(\Stringable::class); $request = new Request(); $request->request->set('object', $objectMock); $this->expectException(\InvalidArgumentException::class); $this->parametersParser->parseRequestValues(['expression' => 'expr:$object.callMethod()'], $request); } } ================================================ FILE: tests/Bundle/Controller/ParametersTest.php ================================================ parameters = new Parameters(); } public function testHasMutableParameters(): void { $this->parameters->replace(); $this->assertSame([], $this->parameters->all()); } public function testHasParameters(): void { $this->parameters->replace([ 'criteria' => 'criteria', 'paginate' => 'paginate', ]); $this->assertSame([ 'criteria' => 'criteria', 'paginate' => 'paginate', ], $this->parameters->all()); } public function testGetsASingleParameterAndSupportsDefaultValue(): void { $this->parameters->replace([ 'criteria' => 'criteria', 'paginate' => 'paginate', ]); $this->assertSame('criteria', $this->parameters->get('criteria')); $this->assertNull($this->parameters->get('sorting')); $this->assertSame('default', $this->parameters->get('sorting', 'default')); } } ================================================ FILE: tests/Bundle/Controller/RedirectHandlerTest.php ================================================ router = $this->createMock(RouterInterface::class); $this->redirectHandler = new RedirectHandler($this->router); $this->configuration = $this->createMock(RequestConfiguration::class); $this->resource = $this->createMock(ResourceInterface::class); } public function test_it_redirects_to_show_route(): void { $this->configuration ->method('getRedirectRoute') ->with('show') ->willReturn('app_resource_show'); $this->configuration ->method('getRedirectParameters') ->willReturn(['id' => 1]); $this->router ->method('generate') ->with('app_resource_show', ['id' => 1]) ->willReturn('/resource/1'); $response = $this->redirectHandler->redirectToResource($this->configuration, $this->resource); $this->assertInstanceOf(RedirectResponse::class, $response); $this->assertEquals('/resource/1', $response->getTargetUrl()); } public function test_it_falls_back_to_index_if_show_route_is_not_found(): void { $this->configuration ->method('getRedirectRoute') ->willReturnMap([ ['show', 'app_resource_show'], ['index', 'app_resource_index'], ]); $this->configuration ->method('getRedirectParameters') ->willReturn([]); $this->router ->method('generate') ->willReturnCallback(function (string $route) { if ($route === 'app_resource_show') { throw new RouteNotFoundException(); } return '/resource/'; }); $response = $this->redirectHandler->redirectToResource($this->configuration, $this->resource); $this->assertInstanceOf(RedirectResponse::class, $response); $this->assertEquals('/resource/', $response->getTargetUrl()); } public function test_it_redirects_to_index(): void { $this->configuration ->method('getRedirectRoute') ->with('index') ->willReturn('app_resource_index'); $this->configuration ->method('getRedirectParameters') ->willReturn([]); $this->router ->method('generate') ->with('app_resource_index', []) ->willReturn('/resource/'); $response = $this->redirectHandler->redirectToIndex($this->configuration); $this->assertInstanceOf(RedirectResponse::class, $response); $this->assertEquals('/resource/', $response->getTargetUrl()); } public function test_it_redirects_to_referer(): void { $this->configuration ->method('getRedirectReferer') ->willReturn('/previous-page'); $response = $this->redirectHandler->redirectToRoute($this->configuration, 'referer'); $this->assertInstanceOf(RedirectResponse::class, $response); $this->assertEquals('/previous-page', $response->getTargetUrl()); } public function test_it_returns_header_redirection(): void { $this->configuration ->method('isHeaderRedirection') ->willReturn(true); $this->configuration ->method('getRedirectHash') ->willReturn('#hash'); $response = $this->redirectHandler->redirect($this->configuration, '/resource/1'); $this->assertInstanceOf(Response::class, $response); $this->assertEquals('', $response->getContent()); $this->assertEquals('/resource/1#hash', $response->headers->get('X-SYLIUS-LOCATION')); } public function test_it_returns_standard_redirect_response(): void { $this->configuration ->method('isHeaderRedirection') ->willReturn(false); $this->configuration ->method('getRedirectHash') ->willReturn('#hash'); $response = $this->redirectHandler->redirect($this->configuration, '/resource/1'); $this->assertInstanceOf(RedirectResponse::class, $response); $this->assertEquals('/resource/1#hash', $response->getTargetUrl()); } } ================================================ FILE: tests/Bundle/Controller/RequestConfigurationFactoryTest.php ================================================ parametersParserMock = $this->createMock(ParametersParserInterface::class); $this->requestConfigurationFactory = new RequestConfigurationFactory($this->parametersParserMock, RequestConfiguration::class); } public function testImplementsRequestConfigurationFactoryInterface(): void { $this->assertInstanceOf(RequestConfigurationFactoryInterface::class, $this->requestConfigurationFactory); } public function testCreatesConfigurationFromResourceMetadataAndRequest(): void { /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var HeaderBag|MockObject $headersBagMock */ $headersBagMock = $this->createMock(HeaderBag::class); /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $requestMock->headers = $headersBagMock; $requestMock->attributes = $attributesBagMock; $headersBagMock->expects($this->any())->method('all')->with('Accept')->willReturn([]); $attributesBagMock->expects($this->once())->method('get')->with('_sylius', [])->willReturn(['template' => ':Product:show.html.twig']); $this->parametersParserMock->expects($this->once())->method('parseRequestValues')->with(['template' => ':Product:show.html.twig'], $requestMock) ->willReturn(['template' => ':Product:list.html.twig']) ; $this->assertInstanceOf(RequestConfiguration::class, $this->requestConfigurationFactory->create($metadataMock, $requestMock)); } public function testCreatesConfigurationWithoutDefaultSettings(): void { /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var HeaderBag|MockObject $headersBagMock */ $headersBagMock = $this->createMock(HeaderBag::class); /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $requestMock->headers = $headersBagMock; $requestMock->attributes = $attributesBagMock; $headersBagMock->expects($this->any())->method('all')->with('Accept')->willReturn([]); $attributesBagMock->expects($this->once())->method('get')->with('_sylius', [])->willReturn(['template' => ':Product:list.html.twig']); $this->parametersParserMock->expects($this->once())->method('parseRequestValues')->with(['template' => ':Product:list.html.twig'], $requestMock) ->willReturn(['template' => ':Product:list.html.twig']) ; $this->assertFalse($this->requestConfigurationFactory->create($metadataMock, $requestMock)->isSortable()); } public function testCreatesConfigurationForSerializationGroupFromSingleHeader(): void { /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var HeaderBag|MockObject $headersBagMock */ $headersBagMock = $this->createMock(HeaderBag::class); /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $requestMock->headers = $headersBagMock; $requestMock->attributes = $attributesBagMock; $attributesBagMock->expects($this->once())->method('get')->with('_sylius', [])->willReturn([ 'allowed_serialization_groups' => ['Default', 'Detailed', 'Other'], ]); $headersBagMock->expects($this->any())->method('all')->with('Accept')->willReturn(['groups=Default,Detailed']); $this->parametersParserMock->expects($this->once())->method('parseRequestValues')->with([ 'allowed_serialization_groups' => ['Default', 'Detailed', 'Other'], 'serialization_groups' => ['Default', 'Detailed'], ], $requestMock) ->willReturn(['serialization_groups' => ['Default', 'Detailed']]) ; $this->assertSame(['Default', 'Detailed'], $this->requestConfigurationFactory->create($metadataMock, $requestMock)->getSerializationGroups()); } public function testCreatesConfigurationForSerializationGroupFromMultipleHeaders(): void { /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var HeaderBag|MockObject $headersBagMock */ $headersBagMock = $this->createMock(HeaderBag::class); /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $requestMock->headers = $headersBagMock; $requestMock->attributes = $attributesBagMock; $attributesBagMock->expects($this->once())->method('get')->with('_sylius', [])->willReturn([ 'allowed_serialization_groups' => ['Default', 'Detailed', 'Other'], ]); $headersBagMock->expects($this->any())->method('all')->with('Accept')->willReturn(['application/json', 'groups=Default,Detailed']); $this->parametersParserMock->expects($this->once())->method('parseRequestValues')->with([ 'allowed_serialization_groups' => ['Default', 'Detailed', 'Other'], 'serialization_groups' => ['Default', 'Detailed'], ], $requestMock) ->willReturn(['serialization_groups' => ['Default', 'Detailed']]) ; $this->assertSame(['Default', 'Detailed'], $this->requestConfigurationFactory->create($metadataMock, $requestMock)->getSerializationGroups()); } public function testCreatesConfigurationUsingOnlyThoseSerializationGroupsThatAreAllowed(): void { /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var HeaderBag|MockObject $headersBagMock */ $headersBagMock = $this->createMock(HeaderBag::class); /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $requestMock->headers = $headersBagMock; $requestMock->attributes = $attributesBagMock; $attributesBagMock->expects($this->once())->method('get')->with('_sylius', [])->willReturn([ 'allowed_serialization_groups' => ['Default'], ]); $headersBagMock->expects($this->any())->method('all')->with('Accept')->willReturn(['application/json', 'groups=Default,Detailed']); $this->parametersParserMock->expects($this->once())->method('parseRequestValues')->with([ 'allowed_serialization_groups' => ['Default'], 'serialization_groups' => ['Default'], ], $requestMock) ->willReturn(['serialization_groups' => ['Default']]) ; $this->assertSame(['Default'], $this->requestConfigurationFactory->create($metadataMock, $requestMock)->getSerializationGroups()); } public function testCreatesConfigurationUsingOnlyThoseSerializationGroupsThatAreAllowedOrDefinedAsDefault(): void { /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var HeaderBag|MockObject $headersBagMock */ $headersBagMock = $this->createMock(HeaderBag::class); /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $requestMock->headers = $headersBagMock; $requestMock->attributes = $attributesBagMock; $attributesBagMock->expects($this->once())->method('get')->with('_sylius', [])->willReturn([ 'allowed_serialization_groups' => ['Default'], 'serialization_groups' => ['Detailed'], ]); $headersBagMock->expects($this->any())->method('all')->with('Accept')->willReturn(['application/json', 'groups=Default,Detailed,Other']); $this->parametersParserMock->expects($this->once())->method('parseRequestValues')->with([ 'allowed_serialization_groups' => ['Default'], 'serialization_groups' => ['Default', 'Detailed'], ], $requestMock) ->willReturn(['serialization_groups' => ['Default', 'Detailed']]) ; $this->assertSame(['Default', 'Detailed'], $this->requestConfigurationFactory->create($metadataMock, $requestMock)->getSerializationGroups()); } public function testCreatesConfigurationUsingOnlyThoseSerializationGroupsThatAreDefinedAsDefault(): void { /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var HeaderBag|MockObject $headersBagMock */ $headersBagMock = $this->createMock(HeaderBag::class); /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $requestMock->headers = $headersBagMock; $requestMock->attributes = $attributesBagMock; $attributesBagMock->expects($this->once())->method('get')->with('_sylius', [])->willReturn([ 'serialization_groups' => ['Detailed'], ]); $headersBagMock->expects($this->any())->method('all')->with('Accept')->willReturn(['application/json', 'groups=Default,Detailed,Other']); $this->parametersParserMock->expects($this->once())->method('parseRequestValues')->with(['serialization_groups' => ['Detailed']], $requestMock) ->willReturn(['serialization_groups' => ['Detailed']]) ; $this->assertSame(['Detailed'], $this->requestConfigurationFactory->create($metadataMock, $requestMock)->getSerializationGroups()); } public function testCreatesConfigurationForSerializationVersionFromSingleHeader(): void { /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var HeaderBag|MockObject $headersBagMock */ $headersBagMock = $this->createMock(HeaderBag::class); /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $requestMock->headers = $headersBagMock; $requestMock->attributes = $attributesBagMock; $headersBagMock->expects($this->any())->method('all')->with('Accept')->willReturn(['version=1.0.0']); $attributesBagMock->expects($this->once())->method('get')->with('_sylius', [])->willReturn([]); $this->parametersParserMock->expects($this->once())->method('parseRequestValues')->with(['serialization_version' => '1.0.0'], $requestMock) ->willReturn(['template' => ':Product:list.html.twig']) ; $this->assertFalse($this->requestConfigurationFactory->create($metadataMock, $requestMock)->isSortable()); } public function testCreatesConfigurationForSerializationVersionFromMultipleHeaders(): void { /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var HeaderBag|MockObject $headersBagMock */ $headersBagMock = $this->createMock(HeaderBag::class); /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $requestMock->headers = $headersBagMock; $requestMock->attributes = $attributesBagMock; $headersBagMock->expects($this->any())->method('all')->with('Accept')->willReturn(['application/xml', 'version=1.0.0']); $attributesBagMock->expects($this->once())->method('get')->with('_sylius', [])->willReturn([]); $this->parametersParserMock->expects($this->once())->method('parseRequestValues')->with(['serialization_version' => '1.0.0'], $requestMock) ->willReturn(['template' => ':Product:list.html.twig']) ; $this->assertFalse($this->requestConfigurationFactory->create($metadataMock, $requestMock)->isSortable()); } public function testCreatesConfigurationWithDefaultSettings(): void { /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var HeaderBag|MockObject $headersBagMock */ $headersBagMock = $this->createMock(HeaderBag::class); /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $this->requestConfigurationFactory = new RequestConfigurationFactory($this->parametersParserMock, RequestConfiguration::class, ['sortable' => true]); $requestMock->headers = $headersBagMock; $requestMock->attributes = $attributesBagMock; $headersBagMock->expects($this->any())->method('all')->with('Accept')->willReturn([]); $attributesBagMock->expects($this->once())->method('get')->with('_sylius', [])->willReturn(['template' => ':Product:list.html.twig']); $this->parametersParserMock->expects($this->once())->method('parseRequestValues')->with(['template' => ':Product:list.html.twig', 'sortable' => true], $requestMock) ->willReturn(['template' => ':Product:list.html.twig', 'sortable' => true]) ; $this->assertTrue($this->requestConfigurationFactory->create($metadataMock, $requestMock)->isSortable()); } } ================================================ FILE: tests/Bundle/Controller/RequestConfigurationTest.php ================================================ metadataMock = $this->createMock(MetadataInterface::class); $this->requestMock = $this->createMock(Request::class); $this->parametersMock = $this->createMock(Parameters::class); $this->requestConfiguration = new RequestConfiguration($this->metadataMock, $this->requestMock, $this->parametersMock); } public function testHasRequest(): void { $this->assertSame($this->requestMock, $this->requestConfiguration->getRequest()); } public function testHasMetadata(): void { $this->assertSame($this->metadataMock, $this->requestConfiguration->getMetadata()); } public function testHasParameters(): void { $this->assertSame($this->parametersMock, $this->requestConfiguration->getParameters()); } public function testChecksIfItsAHtmlRequest(): void { $this->requestMock->expects($this->once())->method('getRequestFormat')->willReturn('html'); $this->assertTrue($this->requestConfiguration->isHtmlRequest()); } public function testChecksIfItsNotAHtmlRequest(): void { $this->requestMock->expects($this->once())->method('getRequestFormat')->willReturn('json'); $this->assertFalse($this->requestConfiguration->isHtmlRequest()); } public function testReturnsDefaultTemplateNames(): void { $this->metadataMock->expects($this->exactly(5))->method('getTemplatesNamespace')->willReturn('@SyliusAdmin/Product'); $this->assertSame('@SyliusAdmin/Product/index.html.twig', $this->requestConfiguration->getDefaultTemplate('index.html')); $this->assertSame('@SyliusAdmin/Product/show.html.twig', $this->requestConfiguration->getDefaultTemplate('show.html')); $this->assertSame('@SyliusAdmin/Product/create.html.twig', $this->requestConfiguration->getDefaultTemplate('create.html')); $this->assertSame('@SyliusAdmin/Product/update.html.twig', $this->requestConfiguration->getDefaultTemplate('update.html')); $this->assertSame('@SyliusAdmin/Product/custom.html.twig', $this->requestConfiguration->getDefaultTemplate('custom.html')); } public function testReturnsDefaultTemplateNamesForADirectoryBasedTemplates(): void { $this->metadataMock->expects($this->exactly(5))->method('getTemplatesNamespace')->willReturn('book/Backend'); $this->assertSame('book/Backend/index.html.twig', $this->requestConfiguration->getDefaultTemplate('index.html')); $this->assertSame('book/Backend/show.html.twig', $this->requestConfiguration->getDefaultTemplate('show.html')); $this->assertSame('book/Backend/create.html.twig', $this->requestConfiguration->getDefaultTemplate('create.html')); $this->assertSame('book/Backend/update.html.twig', $this->requestConfiguration->getDefaultTemplate('update.html')); $this->assertSame('book/Backend/custom.html.twig', $this->requestConfiguration->getDefaultTemplate('custom.html')); } public function testTakesTheCustomTemplateIfSpecified(): void { $this->metadataMock->expects($this->once())->method('getTemplatesNamespace')->willReturn('@SyliusAdmin/Product'); $this->parametersMock->expects($this->once())->method('get')->with('template', '@SyliusAdmin/Product/foo.html.twig')->willReturn('Product/show.html.twig'); $this->assertSame('Product/show.html.twig', $this->requestConfiguration->getTemplate('foo.html')); } public function testFormTypeAndOptionsArrayWithTypeOnly(): void { $this->parametersMock->method('get')->willReturn(['type' => 'sylius_custom_resource']); $this->assertSame('sylius_custom_resource', $this->requestConfiguration->getFormType()); $this->assertSame([], $this->requestConfiguration->getFormOptions()); } public function testFormTypeAndOptionsString(): void { $this->parametersMock->method('get')->willReturn('sylius_custom_resource'); $this->assertSame('sylius_custom_resource', $this->requestConfiguration->getFormType()); $this->assertSame([], $this->requestConfiguration->getFormOptions()); } public function testFormTypeAndOptionsArrayWithTypeAndOptions(): void { $this->parametersMock->method('get')->willReturn([ 'type' => 'sylius_custom_resource', 'options' => ['key' => 'value'], ]); $this->assertSame('sylius_custom_resource', $this->requestConfiguration->getFormType()); $this->assertSame(['key' => 'value'], $this->requestConfiguration->getFormOptions()); } public function testFormTypeAndOptionsFallbackToMetadataWhenEmpty(): void { $this->parametersMock->method('get')->willReturn([]); $this->metadataMock->method('getClass')->with('form')->willReturn('\Fully\Qualified\ClassName'); $this->assertSame('\Fully\Qualified\ClassName', $this->requestConfiguration->getFormType()); $this->assertSame([], $this->requestConfiguration->getFormOptions()); } public function testFormTypeAndOptionsFallbackToMetadataWithOptionsOnly(): void { $this->parametersMock->method('get')->willReturn(['options' => ['key' => 'value']]); $this->metadataMock->method('getClass')->with('form')->willReturn('\Fully\Qualified\ClassName'); $this->assertSame('\Fully\Qualified\ClassName', $this->requestConfiguration->getFormType()); $this->assertSame(['key' => 'value'], $this->requestConfiguration->getFormOptions()); } public function testGeneratesFormTypeWithArrayConfiguration(): void { $this->parametersMock->expects($this->exactly(2))->method('get')->with('form')->willReturn(['type' => 'sylius_product', 'options' => ['validation_groups' => ['sylius']]]); $this->assertSame('sylius_product', $this->requestConfiguration->getFormType()); $this->assertSame(['validation_groups' => ['sylius']], $this->requestConfiguration->getFormOptions()); } public function testGeneratesRouteNamesWithoutSection(): void { $this->metadataMock->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->method('getName')->willReturn('product'); $this->parametersMock->method('get')->with('section')->willReturn(null); $this->assertSame('sylius_product_index', $this->requestConfiguration->getRouteName('index')); $this->assertSame('sylius_product_show', $this->requestConfiguration->getRouteName('show')); $this->assertSame('sylius_product_custom', $this->requestConfiguration->getRouteName('custom')); } public function testGeneratesRouteNamesWithSection(): void { $this->metadataMock->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->method('getName')->willReturn('product'); $this->parametersMock->method('get')->with('section')->willReturn('admin'); $this->assertSame('sylius_admin_product_index', $this->requestConfiguration->getRouteName('index')); $this->assertSame('sylius_admin_product_show', $this->requestConfiguration->getRouteName('show')); $this->assertSame('sylius_admin_product_custom', $this->requestConfiguration->getRouteName('custom')); } public function testGeneratesRedirectReferer(): void { /** @var HeaderBag|MockObject $bagMock */ $bagMock = $this->createMock(HeaderBag::class); $this->requestMock->headers = $bagMock; $bagMock->expects($this->once())->method('get')->with('referer')->willReturn('http://myurl.com'); $this->parametersMock->expects($this->once())->method('get')->with('redirect')->willReturn(['referer' => 'http://myurl.com']); $this->assertSame('http://myurl.com', $this->requestConfiguration->getRedirectReferer()); } public function testGeneratesRedirectRouteWithoutRedirect(): void { $this->metadataMock->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->method('getName')->willReturn('product'); $this->parametersMock ->method('get') ->willReturnMap([ ['section', null, null], ['redirect', null, null], ]); $this->assertSame('sylius_product_index', $this->requestConfiguration->getRedirectRoute('index')); } public function testGeneratesRedirectRouteWithRedirectAsArray(): void { $this->metadataMock->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->method('getName')->willReturn('product'); $this->parametersMock ->method('get') ->willReturnMap([ ['section', null, null], ['redirect', null, ['route' => 'myRoute']], ]); $this->assertSame('myRoute', $this->requestConfiguration->getRedirectRoute('show')); } public function testGeneratesRedirectRouteWithRedirectAsString(): void { $this->metadataMock->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->method('getName')->willReturn('product'); $this->parametersMock ->method('get') ->willReturnMap([ ['section', null, null], ['redirect', null, 'myRoute'], ]); $this->assertSame('myRoute', $this->requestConfiguration->getRedirectRoute('custom')); } public function testRedirectRouteUsesSection(): void { $this->metadataMock->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->method('getName')->willReturn('product'); $this->parametersMock->method('get')->willReturnMap([ ['section', null, 'admin'], ['redirect', null, null], ]); $this->assertSame('sylius_admin_product_index', $this->requestConfiguration->getRedirectRoute('index')); } public function testRedirectRouteOverriddenWithArray(): void { $this->metadataMock->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->method('getName')->willReturn('product'); $this->parametersMock->method('get')->with('redirect')->willReturn(['route' => 'myRoute']); $this->assertSame('myRoute', $this->requestConfiguration->getRedirectRoute('show')); } public function testRedirectRouteOverriddenWithString(): void { $this->metadataMock->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->method('getName')->willReturn('product'); $this->parametersMock->method('get')->with('redirect')->willReturn('myRoute'); $this->assertSame('myRoute', $this->requestConfiguration->getRedirectRoute('custom')); } public function testReturnsEmptyRedirectParametersWhenRedirectIsNull(): void { $this->parametersMock->method('get')->willReturnMap([ ['vars', [], []], ['redirect', null, null], ]); $this->assertSame([], $this->requestConfiguration->getVars()); $this->assertSame([], $this->requestConfiguration->getRedirectParameters()); } public function testReturnsEmptyRedirectParametersWhenRedirectIsString(): void { $this->parametersMock->method('get')->willReturn(['redirect' => 'string']); $this->assertSame([], $this->requestConfiguration->getRedirectParameters()); } public function testReturnsEmptyRedirectParametersWhenRedirectHasEmptyArray(): void { $this->parametersMock->method('get')->willReturn(['redirect' => ['parameters' => []]]); $this->assertSame([], $this->requestConfiguration->getRedirectParameters()); } public function testReturnsRedirectParametersWhenPresent(): void { $this->parametersMock->method('get')->willReturn(['redirect' => ['parameters' => ['myParameter']]]); $this->assertSame(['myParameter'], $this->requestConfiguration->getRedirectParameters()); } public function testMergesExtraVarsWithRedirectParameters(): void { $extraVars = ['redirect' => ['parameters' => ['myExtraParameter']]]; $this->parametersMock->method('get')->willReturnMap([ ['vars', [], $extraVars], ['redirect', null, ['parameters' => ['myParameter']]], ]); $this->assertSame($extraVars, $this->requestConfiguration->getVars()); $this->assertSame(['myParameter', 'myExtraParameter'], $this->requestConfiguration->getRedirectParameters(new \stdClass())); } public function testIsLimitedReturnsTrueWhenLimitIsSet(): void { $this->parametersMock->method('get')->willReturn(10); $this->assertTrue($this->requestConfiguration->isLimited()); } public function testIsLimitedReturnsFalseWhenLimitIsNotSet(): void { $this->parametersMock->method('get')->willReturn(null); $this->assertFalse($this->requestConfiguration->isLimited()); } public function testGetLimitReturnsValue(): void { $this->parametersMock->method('get')->willReturnMap([ ['limit', false, true], ['limit', 10, 10], ]); $this->assertSame(10, $this->requestConfiguration->getLimit()); } public function testGetLimitReturnsNullWhenNotSet(): void { $this->parametersMock->method('get')->willReturnMap([ ['limit', false, false], ['limit', 10, null], ]); $this->assertNull($this->requestConfiguration->getLimit()); } public function testIsPaginatedReturnsTrueWhenLimitIsPositive(): void { $this->parametersMock->method('get')->willReturn(10); $this->assertTrue($this->requestConfiguration->isPaginated()); } public function testIsPaginatedReturnsTrueWhenLimitIsZero(): void { $this->parametersMock->method('get')->willReturn(0); $this->assertTrue($this->requestConfiguration->isPaginated()); } public function testIsPaginatedReturnsFalseWhenLimitIsNull(): void { $this->parametersMock->method('get')->willReturn(null); $this->assertFalse($this->requestConfiguration->isPaginated()); } public function testIsPaginatedReturnsFalseWhenLimitIsFalse(): void { $this->parametersMock->method('get')->willReturn(false); $this->assertFalse($this->requestConfiguration->isPaginated()); } public function testGetPaginationMaxPerPageReturnsCustomValue(): void { $this->parametersMock->method('get')->with('paginate', 10)->willReturn(20); $this->assertSame(20, $this->requestConfiguration->getPaginationMaxPerPage()); } public function testGetPaginationMaxPerPageReturnsDefaultValue(): void { $this->parametersMock->method('get')->with('paginate', 10)->willReturn(10); $this->assertSame(10, $this->requestConfiguration->getPaginationMaxPerPage()); } public function testIsFilterableReturnsTrue(): void { $this->parametersMock->method('get')->willReturn(true); $this->assertTrue($this->requestConfiguration->isFilterable()); } public function testIsFilterableReturnsFalse(): void { $this->parametersMock->method('get')->willReturn(null); $this->assertFalse($this->requestConfiguration->isFilterable()); } public function testHasNoFilterableParameter(): void { $defaultCriteria = ['property' => 'myValue']; $this->parametersMock ->method('get') ->willReturnMap([ ['criteria', [], []], ['filterable', false, false], ]); $this->assertIsIterable($this->requestConfiguration->getCriteria($defaultCriteria)); $this->assertCount(1, $this->requestConfiguration->getCriteria($defaultCriteria)); } public function testHasCriteriaParameter(): void { /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $queryBag = new InputBag(); $requestBag = new InputBag(); $criteria = ['property' => 'myNewValue']; $this->requestMock->attributes = $attributesBagMock; $this->requestMock->query = $queryBag; $this->requestMock->request = $requestBag; $this->parametersMock ->method('get') ->willReturnMap([ ['criteria', [], []], ['filterable', false, true], ]); $attributesBagMock->expects($this->once())->method('get')->with('criteria', $this->requestMock)->willReturn($this->requestMock); $queryBag->set('criteria', $criteria); $requestBag->set('criteria', []); $this->assertSame($criteria, $this->requestConfiguration->getCriteria()); } public function testHasCriteriaParameterInRequest(): void { /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $queryBag = new InputBag(); $requestBag = new InputBag(); $criteria = ['property' => 'myNewValue']; $this->requestMock->attributes = $attributesBagMock; $this->requestMock->query = $queryBag; $this->requestMock->request = $requestBag; $this->parametersMock ->method('get') ->willReturnMap([ ['criteria', [], []], ['filterable', false, true], ]); $attributesBagMock->expects($this->once())->method('get')->with('criteria', $this->requestMock)->willReturn($this->requestMock); $requestBag->set('criteria', $criteria); $this->assertSame($criteria, $this->requestConfiguration->getCriteria()); } public function testCriteriaIsOverriddenByQueryParameters(): void { $attributesBagMock = $this->createMock(ParameterBag::class); $queryBag = new InputBag(); $requestBag = new InputBag(); $criteria = ['property' => 'myValue']; $overriddenCriteria = ['other_property' => 'myNewValue']; $expected = ['property' => 'myValue', 'other_property' => 'myNewValue']; $this->requestMock->attributes = $attributesBagMock; $this->requestMock->query = $queryBag; $this->requestMock->request = $requestBag; $this->parametersMock->method('get')->willReturnMap([ ['filterable', false, true], ['criteria', [], $criteria], ]); $attributesBagMock->method('get')->with('criteria', $this->requestMock)->willReturn($this->requestMock); $queryBag->set('criteria', $overriddenCriteria); $requestBag->set('criteria', []); $this->assertSame($expected, $this->requestConfiguration->getCriteria()); } public function testCriteriaMergesWithDefaultCriteriaWhenOverridden(): void { $attributesBagMock = $this->createMock(ParameterBag::class); $queryBag = new InputBag(); $requestBag = new InputBag(); $criteria = ['property' => 'myValue']; $overriddenCriteria = ['other_property' => 'myNewValue']; $defaultCriteria = ['slug' => 'foo']; $expected = ['property' => 'myValue', 'slug' => 'foo', 'other_property' => 'myNewValue']; $this->requestMock->attributes = $attributesBagMock; $this->requestMock->query = $queryBag; $this->requestMock->request = $requestBag; $this->parametersMock->method('get')->willReturnMap([ ['filterable', false, true], ['criteria', [], $criteria], ]); $attributesBagMock->method('get')->with('criteria', $this->requestMock)->willReturn($this->requestMock); $queryBag->set('criteria', $overriddenCriteria); $requestBag->set('criteria', []); $this->assertSame($expected, $this->requestConfiguration->getCriteria($defaultCriteria)); } public function testQueryCriteriaTakesPrecedenceOverDefaultAndRouteCriteria(): void { $attributesBagMock = $this->createMock(ParameterBag::class); $queryBag = new InputBag(); $requestBag = new InputBag(); $criteria = ['filter' => 'route']; $expected = ['filter' => 'request']; $this->requestMock->attributes = $attributesBagMock; $this->requestMock->query = $queryBag; $this->requestMock->request = $requestBag; $this->parametersMock->method('get')->willReturnMap([ ['filterable', false, true], ['criteria', [], $criteria], ]); $attributesBagMock->method('get')->with('criteria', $this->requestMock)->willReturn($this->requestMock); $queryBag->set('criteria', ['filter' => 'request']); $requestBag->set('criteria', []); $this->assertSame($expected, $this->requestConfiguration->getCriteria(['filter' => 'default'])); } public function testResourceIsSortableWhenParameterIsTrue(): void { $this->parametersMock ->method('get') ->with('sortable', false) ->willReturn(true); $this->assertTrue($this->requestConfiguration->isSortable()); } public function testResourceIsNotSortableWhenParameterIsNull(): void { $this->parametersMock ->method('get') ->with('sortable', false) ->willReturn(null); $this->assertFalse($this->requestConfiguration->isSortable()); } public function testHasSortingParameter(): void { /** @var ParameterBag|MockObject $attributesBagMock */ $attributesBagMock = $this->createMock(ParameterBag::class); $queryBag = new InputBag(); $requestBag = new InputBag(); $sorting = ['property' => 'asc']; $this->requestMock->attributes = $attributesBagMock; $this->requestMock->query = $queryBag; $this->requestMock->request = $requestBag; $this->parametersMock->method('get')->willReturnMap([ ['sortable', false, true], ['sorting', [], $sorting], ]); $attributesBagMock->expects($this->once())->method('get')->with('sorting', $this->requestMock)->willReturn($this->requestMock); $queryBag->set('sorting', $sorting); $requestBag->set('sorting', []); $this->assertSame($sorting, $this->requestConfiguration->getSorting()); } public function testHasNoSortableParameter(): void { $defaultSorting = ['property' => 'desc']; $this->parametersMock->method('get')->willReturnMap([ ['sortable', false, false], ['sorting', [], []], ]); $this->assertIsIterable($this->requestConfiguration->getSorting($defaultSorting)); $this->assertCount(1, $this->requestConfiguration->getSorting($defaultSorting)); } public function testSortingIsOverriddenByQueryParameters(): void { $attributesBagMock = $this->createMock(ParameterBag::class); $queryBag = new InputBag(); $requestBag = new InputBag(); $sorting = ['property' => 'desc']; $overriddenSorting = ['other_property' => 'asc']; $expected = ['other_property' => 'asc', 'property' => 'desc']; $this->requestMock->attributes = $attributesBagMock; $this->requestMock->query = $queryBag; $this->requestMock->request = $requestBag; $this->parametersMock->method('get')->willReturnMap([ ['sortable', false, true], ['sorting', [], $sorting], ]); $attributesBagMock->method('get')->with('sorting', $this->requestMock)->willReturn($this->requestMock); $queryBag->set('sorting', $overriddenSorting); $requestBag->set('sorting', []); $this->assertSame($expected, $this->requestConfiguration->getSorting()); } public function testSortingMergesWithDefaultSortingWhenOverridden(): void { $attributesBagMock = $this->createMock(ParameterBag::class); $queryBag = new InputBag(); $requestBag = new InputBag(); $sorting = ['property' => 'desc']; $overriddenSorting = ['other_property' => 'asc']; $defaultSorting = ['slug' => 'foo']; $expected = ['other_property' => 'asc', 'property' => 'desc', 'slug' => 'foo']; $this->requestMock->attributes = $attributesBagMock; $this->requestMock->query = $queryBag; $this->requestMock->request = $requestBag; $this->parametersMock->method('get')->willReturnMap([ ['sortable', false, true], ['sorting', [], $sorting], ]); $attributesBagMock->method('get')->with('sorting', $this->requestMock)->willReturn($this->requestMock); $queryBag->set('sorting', $overriddenSorting); $requestBag->set('sorting', []); $this->assertSame($expected, $this->requestConfiguration->getSorting($defaultSorting)); } public function testQuerySortingTakesPrecedenceOverRouteAndDefaultSorting(): void { $attributesBagMock = $this->createMock(ParameterBag::class); $queryBag = new InputBag(); $requestBag = new InputBag(); $this->requestMock->attributes = $attributesBagMock; $this->requestMock->query = $queryBag; $this->requestMock->request = $requestBag; $this->parametersMock->method('get')->willReturnMap([ ['sortable', false, true], ['sorting', [], ['sort' => 'route']], ]); $attributesBagMock->method('get')->with('sorting', $this->requestMock)->willReturn($this->requestMock); $queryBag->set('sorting', ['sort' => 'request']); $requestBag->set('sorting', []); $this->assertSame(['sort' => 'request'], $this->requestConfiguration->getSorting(['sort' => 'default'])); } public function testGetRepositoryMethodReturnsNullIfRepositoryParamDoesNotExist(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('repository') ->willReturn(false); $this->assertNull($this->requestConfiguration->getRepositoryMethod()); } public function testGetRepositoryMethodReturnsConfiguredMethod(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('repository') ->willReturn(true); $this->parametersMock ->expects($this->once()) ->method('get') ->with('repository') ->willReturn(['method' => 'findAllEnabled']); $this->assertSame('findAllEnabled', $this->requestConfiguration->getRepositoryMethod()); } public function testReturnsEmptyArrayWhenRepositoryConfigurationIsMissing(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('repository') ->willReturn(false); $this->assertSame([], $this->requestConfiguration->getRepositoryArguments()); } public function testReturnsRepositoryArgumentsWhenScalarValueProvided(): void { $repositoryConfiguration = ['arguments' => 'value']; $this->parametersMock ->expects($this->once()) ->method('has') ->with('repository') ->willReturn(true); $this->parametersMock ->expects($this->once()) ->method('get') ->with('repository') ->willReturn($repositoryConfiguration); $this->assertSame(['value'], $this->requestConfiguration->getRepositoryArguments()); } public function testReturnsRepositoryArgumentsWhenArrayProvided(): void { $repositoryConfiguration = ['arguments' => ['foo, bar']]; $this->parametersMock ->expects($this->once()) ->method('has') ->with('repository') ->willReturn(true); $this->parametersMock ->expects($this->once()) ->method('get') ->with('repository') ->willReturn($repositoryConfiguration); $this->assertSame(['foo, bar'], $this->requestConfiguration->getRepositoryArguments()); } public function testReturnsNullWhenFactoryConfigurationIsMissing(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('factory') ->willReturn(false); $this->assertNull($this->requestConfiguration->getFactoryMethod()); } public function testReturnsFactoryMethodWhenConfigured(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('factory') ->willReturn(true); $this->parametersMock ->expects($this->once()) ->method('get') ->with('factory') ->willReturn(['method' => 'createForPromotion']); $this->assertSame('createForPromotion', $this->requestConfiguration->getFactoryMethod()); } public function testReturnsEmptyArrayWhenFactoryConfigurationIsMissing(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('factory') ->willReturn(false); $this->assertSame([], $this->requestConfiguration->getFactoryArguments()); } public function testReturnsFactoryArgumentsAsArrayWhenConfiguredWithScalar(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('factory') ->willReturn(true); $this->parametersMock ->expects($this->once()) ->method('get') ->with('factory') ->willReturn(['arguments' => 'value']); $this->assertSame(['value'], $this->requestConfiguration->getFactoryArguments()); } public function testReturnsFactoryArgumentsAsArrayWhenConfiguredWithArray(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('factory') ->willReturn(true); $this->parametersMock ->expects($this->once()) ->method('get') ->with('factory') ->willReturn(['arguments' => ['foo, bar']]); $this->assertSame(['foo, bar'], $this->requestConfiguration->getFactoryArguments()); } public function testReturnsDefaultFlashMessageForMessageAction(): void { $this->metadataMock->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->method('getName')->willReturn('product'); $this->parametersMock ->expects($this->once()) ->method('get') ->with('flash', 'sylius.product.message') ->willReturn('sylius.product.message'); $this->assertSame('sylius.product.message', $this->requestConfiguration->getFlashMessage('message')); } public function testReturnsCustomFlashMessageForFlashAction(): void { $this->metadataMock->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->method('getName')->willReturn('product'); $this->parametersMock ->expects($this->once()) ->method('get') ->with('flash', 'sylius.product.flash') ->willReturn('sylius.product.myMessage'); $this->assertSame('sylius.product.myMessage', $this->requestConfiguration->getFlashMessage('flash')); } public function testReturnsDefaultSortablePosition(): void { $this->parametersMock ->expects($this->once()) ->method('get') ->with('sortable_position', 'position') ->willReturn('position'); $this->assertSame('position', $this->requestConfiguration->getSortablePosition()); } public function testReturnsCustomSortablePosition(): void { $this->parametersMock ->expects($this->once()) ->method('get') ->with('sortable_position', 'position') ->willReturn('myPosition'); $this->assertSame('myPosition', $this->requestConfiguration->getSortablePosition()); } public function testShouldNotHavePermissionWhenPermissionIsFalse(): void { $this->parametersMock ->expects($this->once()) ->method('get') ->with('permission', false) ->willReturn(false); $this->assertFalse($this->requestConfiguration->hasPermission()); } public function testShouldHavePermissionWhenPermissionIsCustomString(): void { $this->parametersMock ->expects($this->once()) ->method('get') ->with('permission', false) ->willReturn('custom_permission'); $this->assertTrue($this->requestConfiguration->hasPermission()); } public function testGeneratesPermissionName(): void { $this->metadataMock->expects($this->once())->method('getApplicationName')->willReturn('sylius'); $this->metadataMock->expects($this->once())->method('getName')->willReturn('product'); $this->parametersMock->expects($this->once())->method('get')->with('permission')->willReturn(true); $this->assertSame('sylius.product.index', $this->requestConfiguration->getPermission('index')); } public function testTakesPermissionNameFromParametersIfProvided(): void { $this->parametersMock->expects($this->once())->method('get')->with('permission')->willReturn('app.sales_order.view_pricing'); $this->assertSame('app.sales_order.view_pricing', $this->requestConfiguration->getPermission('index')); } public function testThrowsAnExceptionWhenPermissionIsSetAsFalseInParametersButStillTryingToGetIt(): void { $this->parametersMock->expects($this->once())->method('get')->with('permission')->willReturn(null); $this->expectException(\LogicException::class); $this->requestConfiguration->getPermission('index'); } public function testHasEventName(): void { $this->parametersMock->expects($this->once())->method('get')->with('event')->willReturn('foo'); $this->assertSame('foo', $this->requestConfiguration->getEvent()); } public function testReturnsNullWhenSectionIsNotSet(): void { $this->parametersMock ->expects($this->once()) ->method('get') ->with('section') ->willReturn(null); $this->assertNull($this->requestConfiguration->getSection()); } public function testReturnsSectionWhenSet(): void { $this->parametersMock ->expects($this->once()) ->method('get') ->with('section') ->willReturn('admin'); $this->assertSame('admin', $this->requestConfiguration->getSection()); } public function testHasVars(): void { $this->parametersMock->expects($this->once())->method('get')->with('vars', [])->willReturn(['foo' => 'bar']); $this->assertSame(['foo' => 'bar'], $this->requestConfiguration->getVars()); } public function testShouldNotHaveGridWhenGridNotDefined(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('grid') ->willReturn(false); $this->assertFalse($this->requestConfiguration->hasGrid()); } public function testShouldHaveGridWhenGridDefined(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('grid') ->willReturn(true); $this->assertTrue($this->requestConfiguration->hasGrid()); } public function testGetGridReturnsConfiguredValue(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('grid') ->willReturn(true); $this->parametersMock ->expects($this->once()) ->method('get') ->with('grid') ->willReturn('sylius_admin_tax_category'); $this->assertSame('sylius_admin_tax_category', $this->requestConfiguration->getGrid()); } public function testThrowsAnExceptionWhenTryingToRetrieveUndefinedGrid(): void { $this->parametersMock->expects($this->once())->method('has')->with('grid')->willReturn(false); $this->expectException(\LogicException::class); $this->requestConfiguration->getGrid(); } public function testReturnsFalseWhenNoStateMachineConfigured(): void { $this->parametersMock ->expects($this->once()) ->method('has') ->with('state_machine') ->willReturn(false); $this->assertFalse($this->requestConfiguration->hasStateMachine()); } public function testReturnsStateMachineDetailsWhenConfigured(): void { $stateMachineConfig = [ 'graph' => 'sylius_product_review_state', 'transition' => 'approve', ]; $this->parametersMock ->method('has') ->with('state_machine') ->willReturn(true); $this->parametersMock ->method('get') ->with('state_machine') ->willReturn($stateMachineConfig); $this->assertTrue($this->requestConfiguration->hasStateMachine()); $this->assertSame('sylius_product_review_state', $this->requestConfiguration->getStateMachineGraph()); $this->assertSame('approve', $this->requestConfiguration->getStateMachineTransition()); } } ================================================ FILE: tests/Bundle/Controller/ResourceControllerTest.php ================================================ metadataMock = $this->createMock(MetadataInterface::class); $this->requestConfigurationFactoryMock = $this->createMock(RequestConfigurationFactoryInterface::class); $this->viewHandlerMock = $this->createMock(ViewHandlerInterface::class); $this->repositoryMock = $this->createMock(RepositoryInterface::class); $this->factoryMock = $this->createMock(FactoryInterface::class); $this->newResourceFactoryMock = $this->createMock(NewResourceFactoryInterface::class); $this->managerMock = $this->createMock(ObjectManager::class); $this->singleResourceProviderMock = $this->createMock(SingleResourceProviderInterface::class); $this->resourcesCollectionProviderMock = $this->createMock(ResourcesCollectionProviderInterface::class); $this->resourceFormFactoryMock = $this->createMock(ResourceFormFactoryInterface::class); $this->redirectHandlerMock = $this->createMock(RedirectHandlerInterface::class); $this->flashHelperMock = $this->createMock(FlashHelperInterface::class); $this->authorizationCheckerMock = $this->createMock(AuthorizationCheckerInterface::class); $this->eventDispatcherMock = $this->createMock(EventDispatcherInterface::class); $this->stateMachineMock = $this->createMock(StateMachineInterface::class); $this->resourceUpdateHandlerMock = $this->createMock(ResourceUpdateHandlerInterface::class); $this->resourceDeleteHandlerMock = $this->createMock(ResourceDeleteHandlerInterface::class); $this->containerMock = $this->createMock(ContainerInterface::class); $this->resourceController = new ResourceController($this->metadataMock, $this->requestConfigurationFactoryMock, $this->viewHandlerMock, $this->repositoryMock, $this->factoryMock, $this->newResourceFactoryMock, $this->managerMock, $this->singleResourceProviderMock, $this->resourcesCollectionProviderMock, $this->resourceFormFactoryMock, $this->redirectHandlerMock, $this->flashHelperMock, $this->authorizationCheckerMock, $this->eventDispatcherMock, $this->stateMachineMock, $this->resourceUpdateHandlerMock, $this->resourceDeleteHandlerMock); $this->resourceController->setContainer($this->containerMock); } public function testThrowsA403ExceptionIfUserIsUnauthorizedToViewASingleResource(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::SHOW)->willReturn('sylius.product.show'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.show')->willReturn(false); $this->expectException(AccessDeniedException::class); $this->resourceController->showAction($request); } public function testThrowsA404ExceptionIfResourceIsNotFoundBasedOnConfiguration(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $request = new Request(); $this->metadataMock->expects($this->once())->method('getHumanizedName')->willReturn('product'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::SHOW)->willReturn('sylius.product.show'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.show')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn(null); $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage('The "product" has not been found'); $this->resourceController->showAction($request); } public function testReturnsAResponseForHtmlViewOfASingleResource(): void { $configurationMock = $this->createMock(RequestConfiguration::class); $resourceMock = $this->createMock(ResourceInterface::class); $twigMock = $this->createMock(Environment::class); $request = new Request(); $this->metadataMock->expects($this->once())->method('getName')->willReturn('product'); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once()) ->method('getPermission') ->with(ResourceActions::SHOW) ->willReturn('sylius.product.show'); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.show') ->willReturn(true); $this->singleResourceProviderMock ->expects($this->once()) ->method('get') ->with($configurationMock, $this->repositoryMock) ->willReturn($resourceMock); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once()) ->method('getTemplate') ->with(ResourceActions::SHOW . '.html') ->willReturn('@SyliusShop/Product/show.html.twig'); $this->containerMock->method('has') ->willReturnMap([ ['templating', false], ['twig', true], ]); $this->containerMock->method('get')->with('twig')->willReturn($twigMock); $expectedContext = [ 'configuration' => $configurationMock, 'metadata' => $this->metadataMock, 'resource' => $resourceMock, 'product' => $resourceMock, ]; $twigMock->expects($this->once()) ->method('render') ->with('@SyliusShop/Product/show.html.twig', $expectedContext) ->willReturn('rendered'); $response = $this->resourceController->showAction($request); $this->assertInstanceOf(Response::class, $response); $this->assertSame('rendered', $response->getContent()); } public function testReturnsEventResponseIfExistsDuringShow(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::SHOW)->willReturn('sylius.product.show'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.show')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatch')->with(ResourceActions::SHOW, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->expects($this->once())->method('getResponse')->willReturn($responseMock); $configurationMock->expects($this->never())->method('isHtmlRequest'); $this->viewHandlerMock->expects($this->never())->method('handle'); $this->assertSame($responseMock, $this->resourceController->showAction($request)); } public function testReturnsAResponseForNonHtmlViewOfSingleResource(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); $configurationMock = $this->createMock(RequestConfiguration::class); $resourceMock = $this->createMock(ResourceInterface::class); $responseMock = $this->createMock(Response::class); $request = new Request(); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::SHOW)->willReturn('sylius.product.show'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.show')->willReturn(true); $this->singleResourceProviderMock ->expects($this->once()) ->method('get') ->with($configurationMock, $this->repositoryMock) ->willReturn($resourceMock); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $this->eventDispatcherMock->expects($this->once())->method('dispatch')->with(ResourceActions::SHOW, $configurationMock, $resourceMock); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->showAction($request)); } public function testThrowsA403ExceptionIfUserIsUnauthorizedToViewAnIndexOfResources(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::INDEX)->willReturn('sylius.product.index'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.index')->willReturn(false); $this->expectException(AccessDeniedException::class); $this->resourceController->indexAction($request); } public function testReturnsAResponseForHtmlViewOfPaginatedResources(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resource1Mock */ $resource1Mock = $this->createMock(ResourceInterface::class); /** @var ResourceInterface|MockObject $resource2Mock */ $resource2Mock = $this->createMock(ResourceInterface::class); /** @var Environment|MockObject $twigMock */ $twigMock = $this->createMock(Environment::class); $this->metadataMock->expects($this->once())->method('getPluralName')->willReturn('products'); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::INDEX)->willReturn('sylius.product.index'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('getTemplate')->with(ResourceActions::INDEX . '.html')->willReturn('@SyliusShop/Product/index.html.twig'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.index')->willReturn(true); $this->resourcesCollectionProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn([$resource1Mock, $resource2Mock]); $this->eventDispatcherMock->expects($this->once())->method('dispatchMultiple')->with(ResourceActions::INDEX, $configurationMock, [$resource1Mock, $resource2Mock]); $this->containerMock->method('has') ->willReturnMap([ ['templating', false], ['twig', true], ]); $this->containerMock->expects($this->once())->method('get')->with('twig')->willReturn($twigMock); $expectedContext = [ 'configuration' => $configurationMock, 'metadata' => $this->metadataMock, 'resources' => [$resource1Mock, $resource2Mock], 'products' => [$resource1Mock, $resource2Mock], ]; $twigMock->expects($this->once())->method('render')->willReturnMap([['@SyliusShop/Product/index.html.twig', $expectedContext, 'view'], ['@SyliusShop/Product/index.html.twig', $expectedContext]]); $this->resourceController->indexAction($request); } public function testReturnsEventResponseIfExistsDuringIndex(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceInterface|MockObject $resource1Mock */ $resource1Mock = $this->createMock(ResourceInterface::class); /** @var ResourceInterface|MockObject $resource2Mock */ $resource2Mock = $this->createMock(ResourceInterface::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::INDEX)->willReturn('sylius.product.index'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.index')->willReturn(true); $this->resourcesCollectionProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn([$resource1Mock, $resource2Mock]); $this->eventDispatcherMock->expects($this->once())->method('dispatchMultiple')->with(ResourceActions::INDEX, $configurationMock, [$resource1Mock, $resource2Mock])->willReturn($eventMock); $eventMock->expects($this->once())->method('getResponse')->willReturn($responseMock); $configurationMock->expects($this->never())->method('isHtmlRequest'); $this->viewHandlerMock->expects($this->never())->method('handle'); $this->assertSame($responseMock, $this->resourceController->indexAction($request)); } public function testThrowsA403ExceptionIfUserIsUnauthorizedToCreateANewResource(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.create')->willReturn(false); $this->expectException(AccessDeniedException::class); $this->resourceController->createAction($request); } public function testReturnsAHtmlResponseForCreatingNewResourceForm(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var FormView|MockObject $formViewMock */ $formViewMock = $this->createMock(FormView::class); /** @var Environment|MockObject $twigMock */ $twigMock = $this->createMock(Environment::class); $request = new Request(); $request->setMethod('GET'); $this->metadataMock->expects($this->once())->method('getName')->willReturn('product'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('getTemplate')->with(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.create')->willReturn(true); $this->newResourceFactoryMock->expects($this->once())->method('create')->with($configurationMock, $this->factoryMock)->willReturn($newResourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $newResourceMock)->willReturn($formMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchInitializeEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($eventMock); $eventMock->expects($this->once())->method('getResponse')->willReturn(null); $formMock->expects($this->once())->method('createView')->willReturn($formViewMock); $formMock->method('handleRequest')->willReturnMap([[$request, $formMock], [$request]]); $this->containerMock->method('has') ->willReturnMap([ ['templating', false], ['twig', true], ]); $this->containerMock->expects($this->once())->method('get')->with('twig')->willReturn($twigMock); $expectedContext = [ 'configuration' => $configurationMock, 'metadata' => $this->metadataMock, 'resource' => $newResourceMock, 'product' => $newResourceMock, 'form' => $formViewMock, ]; $twigMock->expects($this->once())->method('render')->with('@SyliusShop/Product/create.html.twig', $expectedContext)->willReturn('view'); $twigMock->method('render')->willReturnMap([['@SyliusShop/Product/create.html.twig', $expectedContext, 'view'], ['@SyliusShop/Product/create.html.twig', $expectedContext]]); $this->resourceController->createAction($request); } public function testReturnsAHtmlResponseForInvalidFormDuringResourceCreation(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var FormView|MockObject $formViewMock */ $formViewMock = $this->createMock(FormView::class); /** @var Environment|MockObject $twigMock */ $twigMock = $this->createMock(Environment::class); $request = new Request(); $request->setMethod('POST'); $this->metadataMock->expects($this->once())->method('getName')->willReturn('product'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('getTemplate')->with(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.create')->willReturn(true); $this->newResourceFactoryMock->expects($this->once())->method('create')->with($configurationMock, $this->factoryMock)->willReturn($newResourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $newResourceMock)->willReturn($formMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchInitializeEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($eventMock); $eventMock->expects($this->once())->method('getResponse')->willReturn(null); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->method('isSubmitted')->willReturn(true); $formMock->method('isValid')->willReturn(false); $formMock->expects($this->once())->method('createView')->willReturn($formViewMock); $this->containerMock->method('has') ->willReturnMap([ ['templating', false], ['twig', true], ]); $this->containerMock->expects($this->once())->method('get')->with('twig')->willReturn($twigMock); $expectedContext = [ 'configuration' => $configurationMock, 'metadata' => $this->metadataMock, 'resource' => $newResourceMock, 'product' => $newResourceMock, 'form' => $formViewMock, ]; $twigMock->method('render')->willReturnMap([['@SyliusShop/Product/create.html.twig', $expectedContext, 'view'], ['@SyliusShop/Product/create.html.twig', $expectedContext]]); $this->resourceController->createAction($request); } public function testReturnsAHtmlResponseForNotSubmittedFormDuringResourceCreation(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var FormView|MockObject $formViewMock */ $formViewMock = $this->createMock(FormView::class); /** @var Environment|MockObject $twigMock */ $twigMock = $this->createMock(Environment::class); $request = new Request(); $request->setMethod('POST'); $this->metadataMock->expects($this->once())->method('getName')->willReturn('product'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.create')->willReturn(true); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('getTemplate')->with(ResourceActions::CREATE . '.html')->willReturn('@SyliusShop/Product/create.html.twig'); $this->newResourceFactoryMock->expects($this->once())->method('create')->with($configurationMock, $this->factoryMock)->willReturn($newResourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $newResourceMock)->willReturn($formMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchInitializeEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($eventMock); $eventMock->expects($this->once())->method('getResponse')->willReturn(null); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->method('isSubmitted')->willReturn(false); $formMock->expects($this->once())->method('createView')->willReturn($formViewMock); $this->containerMock->method('has') ->willReturnMap([ ['templating', false], ['twig', true], ]); $this->containerMock->expects($this->once())->method('get')->with('twig')->willReturn($twigMock); $expectedContext = [ 'configuration' => $configurationMock, 'metadata' => $this->metadataMock, 'resource' => $newResourceMock, 'product' => $newResourceMock, 'form' => $formViewMock, ]; $twigMock->method('render')->willReturnMap([['@SyliusShop/Product/create.html.twig', $expectedContext, 'view'], ['@SyliusShop/Product/create.html.twig', $expectedContext]]); $this->resourceController->createAction($request); } public function testReturnsANonHtmlResponseForInvalidFormDuringResourceCreation(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('POST'); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.create') ->willReturn(true); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $this->newResourceFactoryMock ->expects($this->once()) ->method('create') ->with($configurationMock, $this->factoryMock) ->willReturn($newResourceMock); $this->resourceFormFactoryMock ->expects($this->once()) ->method('create') ->with($configurationMock, $newResourceMock) ->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request); $formMock->method('isSubmitted')->willReturn(true); $formMock->method('isValid')->willReturn(false); $expectedView = View::create($formMock, 400); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->with( $this->anything(), $this->callback(function ($view) use ($expectedView) { return $view instanceof View && $view->getData() === $expectedView->getData() && $view->getStatusCode() === $expectedView->getStatusCode(); }), ) ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->createAction($request)); } public function testReturnsANonHtmlResponseForNotSubmittedFormDuringResourceCreation(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('POST'); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.create') ->willReturn(true); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $this->newResourceFactoryMock ->expects($this->once()) ->method('create') ->with($configurationMock, $this->factoryMock) ->willReturn($newResourceMock); $this->resourceFormFactoryMock ->expects($this->once()) ->method('create') ->with($configurationMock, $newResourceMock) ->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request); $formMock->method('isSubmitted')->willReturn(false); $expectedView = View::create($formMock, 400); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->with( $this->anything(), $this->callback(function ($view) use ($expectedView) { return $view instanceof View && $view->getData() === $expectedView->getData() && $view->getStatusCode() === $expectedView->getStatusCode(); }), ) ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->createAction($request)); } public function testDoesNotCreateTheResourceAndRedirectsToIndexForHtmlRequestsStoppedViaEvents(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('POST'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.create')->willReturn(true); $this->newResourceFactoryMock->expects($this->once())->method('create')->with($configurationMock, $this->factoryMock)->willReturn($newResourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $newResourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($newResourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(true); $this->flashHelperMock->expects($this->once())->method('addFlashFromEvent')->with($configurationMock, $eventMock); $eventMock->expects($this->once())->method('getResponse')->willReturn(null); $this->repositoryMock->expects($this->never())->method('add')->with($newResourceMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash'); $this->redirectHandlerMock->expects($this->once())->method('redirectToIndex')->with($configurationMock, $newResourceMock)->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->createAction($request)); } public function testDoesNotCreateTheResourceAndReturnResponseForHtmlRequestsStoppedViaEvents(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('POST'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.create')->willReturn(true); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $this->newResourceFactoryMock->expects($this->once())->method('create')->with($configurationMock, $this->factoryMock)->willReturn($newResourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $newResourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($newResourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(true); $eventMock->expects($this->once())->method('getResponse')->willReturn($responseMock); $this->flashHelperMock->expects($this->once())->method('addFlashFromEvent')->with($configurationMock, $eventMock); $this->repositoryMock->expects($this->never())->method('add')->with($newResourceMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash'); $this->assertSame($responseMock, $this->resourceController->createAction($request)); } public function testRedirectsToNewlyCreatedResource(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $postEventMock */ $postEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('POST'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $configurationMock->expects($this->once())->method('hasStateMachine')->willReturn(true); $configurationMock->method('isHtmlRequest')->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.create')->willReturn(true); $this->newResourceFactoryMock->expects($this->once())->method('create')->with($configurationMock, $this->factoryMock)->willReturn($newResourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $newResourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($newResourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->stateMachineMock->expects($this->once())->method('apply')->with($configurationMock, $newResourceMock); $this->repositoryMock->expects($this->once())->method('add')->with($newResourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPostEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($postEventMock); $postEventMock->expects($this->once())->method('getResponse')->willReturn(null); $this->flashHelperMock->expects($this->once())->method('addSuccessFlash')->with($configurationMock, ResourceActions::CREATE, $newResourceMock); $this->redirectHandlerMock->expects($this->once())->method('redirectToResource')->with($configurationMock, $newResourceMock)->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->createAction($request)); } public function testUsesResponseFromPostCreateEventIfDefined(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $postEventMock */ $postEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('POST'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $configurationMock->expects($this->once())->method('hasStateMachine')->willReturn(true); $configurationMock->method('isHtmlRequest')->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.create')->willReturn(true); $this->newResourceFactoryMock->expects($this->once())->method('create')->with($configurationMock, $this->factoryMock)->willReturn($newResourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $newResourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($newResourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->stateMachineMock->expects($this->once())->method('apply')->with($configurationMock, $newResourceMock); $this->repositoryMock->expects($this->once())->method('add')->with($newResourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPostEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($postEventMock); $this->flashHelperMock->expects($this->once())->method('addSuccessFlash')->with($configurationMock, ResourceActions::CREATE, $newResourceMock); $postEventMock->expects($this->once())->method('getResponse')->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->createAction($request)); } public function testReturnsANonHtmlResponseForCorrectlyCreatedResources(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('POST'); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $configurationMock->expects($this->once())->method('hasStateMachine')->willReturn(true); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.create') ->willReturn(true); $configurationMock->method('isHtmlRequest')->willReturn(false); $this->newResourceFactoryMock ->expects($this->once()) ->method('create') ->with($configurationMock, $this->factoryMock) ->willReturn($newResourceMock); $this->resourceFormFactoryMock ->expects($this->once()) ->method('create') ->with($configurationMock, $newResourceMock) ->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($newResourceMock); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPreEvent') ->with(ResourceActions::CREATE, $configurationMock, $newResourceMock) ->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->stateMachineMock ->expects($this->once()) ->method('apply') ->with($configurationMock, $newResourceMock); $this->repositoryMock ->expects($this->once()) ->method('add') ->with($newResourceMock); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPostEvent') ->with(ResourceActions::CREATE, $configurationMock, $newResourceMock); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash'); $expectedView = View::create($newResourceMock, 201); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->with( $this->anything(), $this->callback(function ($view) use ($expectedView) { return $view instanceof View && $view->getData() === $expectedView->getData() && $view->getStatusCode() === $expectedView->getStatusCode(); }), ) ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->createAction($request)); } public function testDoesNotCreateTheResourceAndThrowsHttpExceptionForNonHtmlRequestsStoppedViaEvent(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); $request = new Request(); $request->setMethod('POST'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.create')->willReturn(true); $this->newResourceFactoryMock->expects($this->once())->method('create')->with($configurationMock, $this->factoryMock)->willReturn($newResourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $newResourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($newResourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($eventMock); $eventMock->expects($this->once())->method('isStopped')->willReturn(true); $eventMock->expects($this->once())->method('getMessage')->willReturn('You cannot add a new product right now.'); $eventMock->expects($this->once())->method('getErrorCode')->willReturn(500); $this->repositoryMock->expects($this->never())->method('add')->with($newResourceMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash'); $this->expectException(HttpException::class); $this->resourceController->createAction($request); } public function testThrowsA403ExceptionIfUserIsUnauthorizedToEditASingleResource(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(false); $this->expectException(AccessDeniedException::class); $this->resourceController->updateAction($request); } public function testThrowsA404ExceptionIfResourceToUpdateIsNotFoundBasedOnConfiguration(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $request = new Request(); $this->metadataMock->expects($this->once())->method('getHumanizedName')->willReturn('product'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn(null); $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage('The "product" has not been found'); $this->resourceController->updateAction($request); } public function testReturnsAHtmlResponseForUpdatingResourceForm(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var FormView|MockObject $formViewMock */ $formViewMock = $this->createMock(FormView::class); /** @var Environment|MockObject $twigMock */ $twigMock = $this->createMock(Environment::class); $request = $this->createMock(Request::class); $request->setMethod('GET'); $this->metadataMock->expects($this->once())->method('getName')->willReturn('product'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('getTemplate')->with(ResourceActions::UPDATE . '.html')->willReturn('@SyliusShop/Product/update.html.twig'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $resourceMock)->willReturn($formMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchInitializeEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->expects($this->once())->method('getResponse')->willReturn(null); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('createView')->willReturn($formViewMock); $this->containerMock->method('has') ->willReturnMap([ ['templating', false], ['twig', true], ]); $this->containerMock->expects($this->once())->method('get')->with('twig')->willReturn($twigMock); $expectedContext = [ 'configuration' => $configurationMock, 'metadata' => $this->metadataMock, 'resource' => $resourceMock, 'product' => $resourceMock, 'form' => $formViewMock, ]; $twigMock->method('render')->willReturnMap([['@SyliusShop/Product/update.html.twig', $expectedContext, 'view'], ['@SyliusShop/Product/update.html.twig', $expectedContext]]); $this->resourceController->updateAction($request); } public function testReturnsAHtmlResponseForInvalidFormDuringResourceUpdate(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var FormView|MockObject $formViewMock */ $formViewMock = $this->createMock(FormView::class); /** @var Environment|MockObject $twigMock */ $twigMock = $this->createMock(Environment::class); $request = $this->createMock(Request::class); $request->setMethod('PUT'); $this->metadataMock->expects($this->once())->method('getName')->willReturn('product'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('getTemplate')->with(ResourceActions::UPDATE . '.html')->willReturn('@SyliusShop/Product/update.html.twig'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $resourceMock)->willReturn($formMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchInitializeEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->expects($this->once())->method('getResponse')->willReturn(null); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->method('isSubmitted')->willReturn(true); $formMock->method('isValid')->willReturn(false); $formMock->expects($this->once())->method('createView')->willReturn($formViewMock); $this->containerMock->method('has') ->willReturnMap([ ['templating', false], ['twig', true], ]); $this->containerMock->expects($this->once())->method('get')->with('twig')->willReturn($twigMock); $expectedContext = [ 'configuration' => $configurationMock, 'metadata' => $this->metadataMock, 'resource' => $resourceMock, 'product' => $resourceMock, 'form' => $formViewMock, ]; $twigMock->method('render')->willReturnMap([['@SyliusShop/Product/update.html.twig', $expectedContext, 'view'], ['@SyliusShop/Product/update.html.twig', $expectedContext]]); $this->resourceController->updateAction($request); } public function testReturnsAHtmlResponseForNotSubmittedFormDuringResourceUpdate(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var FormView|MockObject $formViewMock */ $formViewMock = $this->createMock(FormView::class); /** @var Environment|MockObject $twigMock */ $twigMock = $this->createMock(Environment::class); /** @var Request|MockObject $request */ $request = new Request(); $request->setMethod('PUT'); $this->metadataMock->expects($this->once())->method('getName')->willReturn('product'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('getTemplate')->with(ResourceActions::UPDATE . '.html')->willReturn('@SyliusShop/Product/update.html.twig'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $resourceMock)->willReturn($formMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchInitializeEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->expects($this->once())->method('getResponse')->willReturn(null); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->method('isSubmitted')->willReturn(false); $formMock->expects($this->once())->method('createView')->willReturn($formViewMock); $this->containerMock->method('has') ->willReturnMap([ ['templating', false], ['twig', true], ]); $this->containerMock->expects($this->once())->method('get')->with('twig')->willReturn($twigMock); $expectedContext = [ 'configuration' => $configurationMock, 'metadata' => $this->metadataMock, 'resource' => $resourceMock, 'product' => $resourceMock, 'form' => $formViewMock, ]; $twigMock->method('render')->willReturnMap([['@SyliusShop/Product/update.html.twig', $expectedContext, 'view'], ['@SyliusShop/Product/update.html.twig', $expectedContext]]); $this->resourceController->updateAction($request); } public function testReturnsANonHtmlResponseForInvalidFormDuringResourceUpdate(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('PATCH'); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.update') ->willReturn(true); $this->singleResourceProviderMock ->expects($this->once()) ->method('get') ->with($configurationMock, $this->repositoryMock) ->willReturn($resourceMock); $this->resourceFormFactoryMock ->expects($this->once()) ->method('create') ->with($configurationMock, $resourceMock) ->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request); $formMock->method('isSubmitted')->willReturn(true); $formMock->method('isValid')->willReturn(false); $expectedView = View::create($formMock, 400); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->with( $this->anything(), $this->callback(function ($view) use ($expectedView) { return $view instanceof View && $view->getData() === $expectedView->getData() && $view->getStatusCode() === $expectedView->getStatusCode(); }), ) ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->updateAction($request)); } public function testReturnsANonHtmlResponseForNotSubmittedFormDuringResourceUpdate(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $requestMock) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.update') ->willReturn(true); $this->singleResourceProviderMock ->expects($this->once()) ->method('get') ->with($configurationMock, $this->repositoryMock) ->willReturn($resourceMock); $this->resourceFormFactoryMock ->expects($this->once()) ->method('create') ->with($configurationMock, $resourceMock) ->willReturn($formMock); $requestMock->method('isMethod')->with('PATCH')->willReturn(true); $requestMock->method('getMethod')->willReturn('PATCH'); $formMock->expects($this->once())->method('handleRequest')->with($requestMock); $formMock->method('isSubmitted')->willReturn(false); $expectedView = View::create($formMock, 400); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->with( $this->anything(), $this->callback(function ($view) use ($expectedView) { return $view instanceof View && $view->getData() === $expectedView->getData() && $view->getStatusCode() === $expectedView->getStatusCode(); }), ) ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->updateAction($requestMock)); } public function testDoesNotUpdateTheResourceAndRedirectsToResourceForHtmlRequestIfStoppedViaEvent(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('PUT'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $resourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(true); $eventMock->expects($this->once())->method('getResponse')->willReturn(null); $this->flashHelperMock->expects($this->once())->method('addFlashFromEvent')->with($configurationMock, $eventMock); $this->managerMock->expects($this->never())->method('flush'); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent'); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash'); $this->redirectHandlerMock->expects($this->once())->method('redirectToResource')->with($configurationMock, $resourceMock)->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->updateAction($request)); } public function testRedirectsToUpdatedResource(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var ResourceControllerEvent|MockObject $preEventMock */ $preEventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $postEventMock */ $postEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('PUT'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->method('isHtmlRequest')->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $resourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($preEventMock); $preEventMock->method('isStopped')->willReturn(false); $this->resourceUpdateHandlerMock->expects($this->once())->method('handle')->with($resourceMock, $configurationMock, $this->managerMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPostEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($postEventMock); $postEventMock->expects($this->once())->method('getResponse')->willReturn(null); $this->flashHelperMock->expects($this->once())->method('addSuccessFlash')->with($configurationMock, ResourceActions::UPDATE, $resourceMock); $this->redirectHandlerMock->expects($this->once())->method('redirectToResource')->with($configurationMock, $resourceMock)->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->updateAction($request)); } public function testUsesResponseFromPostUpdateEventIfDefined(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var ResourceControllerEvent|MockObject $preEventMock */ $preEventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $postEventMock */ $postEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('PUT'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->method('isHtmlRequest')->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $resourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($preEventMock); $preEventMock->method('isStopped')->willReturn(false); $this->resourceUpdateHandlerMock->expects($this->once())->method('handle')->with($resourceMock, $configurationMock, $this->managerMock); $this->flashHelperMock->expects($this->once())->method('addSuccessFlash')->with($configurationMock, ResourceActions::UPDATE, $resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPostEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($postEventMock); $postEventMock->expects($this->once())->method('getResponse')->willReturn($redirectResponseMock); $this->redirectHandlerMock->expects($this->never())->method('redirectToResource')->with($configurationMock, $resourceMock); $this->assertSame($redirectResponseMock, $this->resourceController->updateAction($request)); } public function testUsesResponseFromInitializeCreateEventIfDefined(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $newResourceMock */ $newResourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $initializeEventMock */ $initializeEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('GET'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::CREATE)->willReturn('sylius.product.create'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.create')->willReturn(true); $this->newResourceFactoryMock->expects($this->once())->method('create')->with($configurationMock, $this->factoryMock)->willReturn($newResourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $newResourceMock)->willReturn($formMock); $formMock->expects($this->never())->method('createView'); $formMock->method('handleRequest')->willReturnMap([[$request, $formMock], [$request]]); $initializeEventMock->expects($this->once())->method('getResponse')->willReturn($responseMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchInitializeEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock)->willReturn($initializeEventMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPreEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent')->with(ResourceActions::CREATE, $configurationMock, $newResourceMock); $this->redirectHandlerMock->expects($this->never())->method('redirectToResource')->with($configurationMock, $newResourceMock); $this->containerMock->method('has') ->willReturnMap([ ['templating', false], ['twig', true], ]); $this->resourceController->createAction($request); } public function testUsesResponseFromInitializeUpdateEventIfDefined(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var ResourceControllerEvent|MockObject $initializeEventMock */ $initializeEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $resourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock); $this->resourceUpdateHandlerMock->expects($this->never())->method('handle')->with($resourceMock, $configurationMock, $this->managerMock); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash')->with($configurationMock, ResourceActions::UPDATE, $resourceMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock); $this->redirectHandlerMock->expects($this->never())->method('redirectToResource')->with($configurationMock, $resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchInitializeEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($initializeEventMock); $initializeEventMock->expects($this->once())->method('getResponse')->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->updateAction($request)); } public function testReturnsANonHtmlResponseForCorrectlyUpdatedResource(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); /** @var ParameterBagInterface|MockObject $parameterBagMock */ $parameterBagMock = $this->createMock(ParameterBagInterface::class); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $request->setMethod('PUT'); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->method('isHtmlRequest')->willReturn(false); $configurationMock->expects($this->once())->method('getParameters')->willReturn($parameterBagMock); $parameterBagMock->expects($this->once())->method('get')->with('return_content', false)->willReturn(false); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.update') ->willReturn(true); $this->singleResourceProviderMock ->expects($this->once()) ->method('get') ->with($configurationMock, $this->repositoryMock) ->willReturn($resourceMock); $this->resourceFormFactoryMock ->expects($this->once()) ->method('create') ->with($configurationMock, $resourceMock) ->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($resourceMock); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPreEvent') ->with(ResourceActions::UPDATE, $configurationMock, $resourceMock) ->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->resourceUpdateHandlerMock ->expects($this->once()) ->method('handle') ->with($resourceMock, $configurationMock, $this->managerMock); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPostEvent') ->with(ResourceActions::UPDATE, $configurationMock, $resourceMock); $expectedView = View::create(null, 204); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->with( $this->anything(), $this->callback(function ($view) use ($expectedView) { return $view instanceof View && $view->getData() === $expectedView->getData() && $view->getStatusCode() === $expectedView->getStatusCode(); }), ) ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->updateAction($request)); } public function testDoesNotUpdateTheResourceThrowsAHttpExceptionForNonHtmlRequestsStoppedViaEvent(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); $request = new Request(); $request->setMethod('PUT'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $resourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(true); $eventMock->expects($this->once())->method('getMessage')->willReturn('Cannot update this channel.'); $eventMock->expects($this->once())->method('getErrorCode')->willReturn(500); $this->managerMock->expects($this->never())->method('flush'); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent'); $this->expectException(HttpException::class); $this->resourceController->updateAction($request); } public function testAppliesStateMachineTransitionToUpdatedResourceIfConfigured(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var Form|MockObject $formMock */ $formMock = $this->createMock(Form::class); /** @var ResourceControllerEvent|MockObject $preEventMock */ $preEventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $postEventMock */ $postEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); /** @var Request|MockObject $request */ $request = new Request(); $request->setMethod('PUT'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->method('isHtmlRequest')->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->resourceFormFactoryMock->expects($this->once())->method('create')->with($configurationMock, $resourceMock)->willReturn($formMock); $formMock->expects($this->once())->method('handleRequest')->with($request)->willReturn($formMock); $formMock->expects($this->once())->method('isSubmitted')->willReturn(true); $formMock->expects($this->once())->method('isValid')->willReturn(true); $formMock->expects($this->once())->method('getData')->willReturn($resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($preEventMock); $preEventMock->method('isStopped')->willReturn(false); $this->resourceUpdateHandlerMock->expects($this->once())->method('handle')->with($resourceMock, $configurationMock, $this->managerMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPostEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($postEventMock); $postEventMock->expects($this->once())->method('getResponse')->willReturn(null); $this->flashHelperMock->expects($this->once())->method('addSuccessFlash')->with($configurationMock, ResourceActions::UPDATE, $resourceMock); $this->redirectHandlerMock->expects($this->once())->method('redirectToResource')->with($configurationMock, $resourceMock)->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->updateAction($request)); } public function testThrowsA403ExceptionIfUserIsUnauthorizedToDeleteMultipleResources(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::BULK_DELETE)->willReturn('sylius.product.bulk_delete'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.bulk_delete')->willReturn(false); $this->expectException(AccessDeniedException::class); $this->resourceController->bulkDeleteAction($request); } public function testDeletesMultipleResourcesAndRedirectsToIndexForHtmlRequest(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $firstResourceMock */ $firstResourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceInterface|MockObject $secondResourceMock */ $secondResourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $firstPreEventMock */ $firstPreEventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $secondPreEventMock */ $secondPreEventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $firstPostEventMock */ $firstPostEventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $secondPostEventMock */ $secondPostEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::BULK_DELETE)->willReturn('sylius.product.bulk_delete'); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('bulk_delete', 'xyz'))->willReturn(true); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchMultiple') ->with(ResourceActions::BULK_DELETE, $configurationMock, [$firstResourceMock, $secondResourceMock]); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.bulk_delete') ->willReturn(true); $this->resourcesCollectionProviderMock ->expects($this->once()) ->method('get') ->with($configurationMock, $this->repositoryMock) ->willReturn([$firstResourceMock, $secondResourceMock]); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->eventDispatcherMock ->expects($this->exactly(2)) ->method('dispatchPreEvent') ->willReturnCallback(function ($action, $configuration, $resource) use ($firstResourceMock, $secondResourceMock, $firstPreEventMock, $secondPreEventMock) { if ($resource === $firstResourceMock) { return $firstPreEventMock; } if ($resource === $secondResourceMock) { return $secondPreEventMock; } return null; }); $firstPreEventMock->method('isStopped')->willReturn(false); $secondPreEventMock->method('isStopped')->willReturn(false); $this->resourceDeleteHandlerMock ->expects($this->exactly(2)) ->method('handle') ->willReturnCallback(function ($resource, $repository) use ($firstResourceMock, $secondResourceMock) { static $call = 0; ++$call; if ($call === 1) { $this->assertSame($firstResourceMock, $resource); } elseif ($call === 2) { $this->assertSame($secondResourceMock, $resource); } $this->assertSame($this->repositoryMock, $repository); }); $this->eventDispatcherMock ->expects($this->exactly(2)) ->method('dispatchPostEvent') ->willReturnCallback(function ($action, $configuration, $resource) use ($firstResourceMock, $secondResourceMock, $firstPostEventMock, $secondPostEventMock) { if ($resource === $firstResourceMock) { return $firstPostEventMock; } if ($resource === $secondResourceMock) { return $secondPostEventMock; } return null; }); $secondPostEventMock->expects($this->once())->method('getResponse')->willReturn(null); $this->flashHelperMock ->expects($this->once()) ->method('addSuccessFlash') ->with($configurationMock, ResourceActions::BULK_DELETE); $this->redirectHandlerMock ->expects($this->once()) ->method('redirectToIndex') ->with($configurationMock) ->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->bulkDeleteAction($request)); } public function testThrowsA403ExceptionIfUserIsUnauthorizedToDeleteASingleResource(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::DELETE)->willReturn('sylius.product.delete'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.delete')->willReturn(false); $this->expectException(AccessDeniedException::class); $this->resourceController->deleteAction($request); } public function testThrowsA404ExceptionIfResourceForDeletionIsNotFoundBasedOnConfiguration(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $requestMock = new Request(); $this->metadataMock->expects($this->once())->method('getHumanizedName')->willReturn('product'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $requestMock)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::DELETE)->willReturn('sylius.product.delete'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.delete')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn(null); $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage('The "product" has not been found'); $this->resourceController->deleteAction($requestMock); } public function testDeletesAResourceAndRedirectsToIndexByForHtmlRequest(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $postEventMock */ $postEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::DELETE)->willReturn('sylius.product.delete'); $configurationMock->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.delete')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn(1); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::DELETE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->resourceDeleteHandlerMock->expects($this->once())->method('handle')->with($resourceMock, $this->repositoryMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPostEvent')->with(ResourceActions::DELETE, $configurationMock, $resourceMock)->willReturn($postEventMock); $postEventMock->expects($this->once())->method('getResponse')->willReturn(null); $this->flashHelperMock->expects($this->once())->method('addSuccessFlash')->with($configurationMock, ResourceActions::DELETE, $resourceMock); $this->redirectHandlerMock->expects($this->once())->method('redirectToIndex')->with($configurationMock, $resourceMock)->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->deleteAction($request)); } public function testUsesResponseFromPostDeleteEventIfDefined(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $postEventMock */ $postEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::DELETE)->willReturn('sylius.product.delete'); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.delete')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn(1); $configurationMock->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::DELETE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->resourceDeleteHandlerMock->expects($this->once())->method('handle')->with($resourceMock, $this->repositoryMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPostEvent')->with(ResourceActions::DELETE, $configurationMock, $resourceMock)->willReturn($postEventMock); $this->flashHelperMock->expects($this->once())->method('addSuccessFlash')->with($configurationMock, ResourceActions::DELETE, $resourceMock); $postEventMock->expects($this->once())->method('getResponse')->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->deleteAction($request)); } public function testDoesNotDeleteAResourceAndRedirectsToIndexForHtmlRequestsStoppedViaEvent(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::DELETE)->willReturn('sylius.product.delete'); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.delete')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn(1); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::DELETE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(true); $eventMock->expects($this->once())->method('getResponse')->willReturn(null); $this->resourceDeleteHandlerMock->expects($this->never())->method('handle')->with($resourceMock, $this->repositoryMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent')->with(ResourceActions::DELETE, $configurationMock, $resourceMock); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash')->with($configurationMock, ResourceActions::DELETE, $resourceMock); $this->flashHelperMock->expects($this->once())->method('addFlashFromEvent')->with($configurationMock, $eventMock); $this->redirectHandlerMock->expects($this->once())->method('redirectToIndex')->with($configurationMock, $resourceMock)->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->deleteAction($request)); } public function testDoesNotDeleteAResourceAndUsesResponseFromEventIfDefined(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::DELETE)->willReturn('sylius.product.delete'); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.delete')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn(1); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::DELETE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(true); $eventMock->expects($this->once())->method('getResponse')->willReturn($redirectResponseMock); $this->flashHelperMock->expects($this->once())->method('addFlashFromEvent')->with($configurationMock, $eventMock); $this->resourceDeleteHandlerMock->expects($this->never())->method('handle')->with($resourceMock, $this->repositoryMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent')->with(ResourceActions::DELETE, $configurationMock, $resourceMock); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash')->with($configurationMock, ResourceActions::DELETE, $resourceMock); $this->redirectHandlerMock->expects($this->never())->method('redirectToIndex')->with($configurationMock, $resourceMock); $this->assertSame($redirectResponseMock, $this->resourceController->deleteAction($request)); } public function testDoesNotCorrectlyDeleteAResourceAndReturns500ForNotHtmlResponse(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::DELETE)->willReturn('sylius.product.delete'); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock ->expects($this->once()) ->method('isTokenValid') ->with(new CsrfToken('1', 'xyz')) ->willReturn(true); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.delete') ->willReturn(true); $this->singleResourceProviderMock ->expects($this->once()) ->method('get') ->with($configurationMock, $this->repositoryMock) ->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn(1); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPreEvent') ->with(ResourceActions::DELETE, $configurationMock, $resourceMock) ->willReturn($eventMock); $this->resourceDeleteHandlerMock ->expects($this->once()) ->method('handle') ->with($resourceMock, $this->repositoryMock) ->willThrowException(new DeleteHandlingException()); $this->eventDispatcherMock ->expects($this->never()) ->method('dispatchPostEvent'); $expectedView = View::create(null, 500); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->with( $this->anything(), $this->callback(function ($view) use ($expectedView) { return $view instanceof View && $view->getData() === $expectedView->getData() && $view->getStatusCode() === $expectedView->getStatusCode(); }), ) ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->deleteAction($request)); } public function testDeletesAResourceAndReturns204ForNonHtmlRequests(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::DELETE)->willReturn('sylius.product.delete'); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock ->expects($this->once()) ->method('isTokenValid') ->with(new CsrfToken('1', 'xyz')) ->willReturn(true); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.delete') ->willReturn(true); $this->singleResourceProviderMock ->expects($this->once()) ->method('get') ->with($configurationMock, $this->repositoryMock) ->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn(1); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPreEvent') ->with(ResourceActions::DELETE, $configurationMock, $resourceMock) ->willReturn($eventMock); $this->resourceDeleteHandlerMock ->expects($this->once()) ->method('handle') ->with($resourceMock, $this->repositoryMock); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPostEvent') ->with(ResourceActions::DELETE, $configurationMock, $resourceMock); $expectedView = View::create(null, 204); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->with( $this->anything(), $this->callback(function ($view) use ($expectedView) { return $view instanceof View && $view->getData() === $expectedView->getData() && $view->getStatusCode() === $expectedView->getStatusCode(); }), ) ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->deleteAction($request)); } public function testDoesNotDeleteAResourceAndThrowsHttpExceptionForNonHtmlRequestsStoppedViaEvent(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::DELETE)->willReturn('sylius.product.delete'); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.delete')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn(1); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::DELETE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->expects($this->once())->method('isStopped')->willReturn(true); $eventMock->expects($this->once())->method('getMessage')->willReturn('Cannot delete this product.'); $eventMock->expects($this->once())->method('getErrorCode')->willReturn(500); $this->resourceDeleteHandlerMock->expects($this->never())->method('handle')->with($resourceMock, $this->repositoryMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent'); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash'); $this->flashHelperMock->expects($this->never())->method('addFlashFromEvent'); $this->expectException(HttpException::class); $this->resourceController->deleteAction($request); } public function testThrowsA403ExceptionIfCsrfTokenIsInvalidDuringDeleteAction(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::DELETE)->willReturn('sylius.product.delete'); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(false); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.delete')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn(1); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $eventMock->expects($this->never())->method('isStopped'); $this->resourceDeleteHandlerMock->expects($this->never())->method('handle')->with($resourceMock, $this->repositoryMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent'); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash'); $this->flashHelperMock->expects($this->never())->method('addFlashFromEvent'); $this->expectException(HttpException::class); $this->resourceController->deleteAction($request); } public function testThrowsA403ExceptionIfUserIsUnauthorizedToApplyStateMachineTransitionOnResource(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(false); $this->expectException(AccessDeniedException::class); $this->resourceController->applyStateMachineTransitionAction($request); } public function testThrowsA404ExceptionIfResourceIsNotFoundWhenTryingToApplyStateMachineTransition(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); $request = new Request(); $this->metadataMock->expects($this->once())->method('getHumanizedName')->willReturn('product'); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn(null); $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage('The "product" has not been found'); $this->resourceController->applyStateMachineTransitionAction($request); } public function testDoesNotApplyStateMachineTransitionOnResourceIfNotApplicableAndReturns400BadRequest(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ObjectManager|MockObject $objectManagerMock */ $objectManagerMock = $this->createMock(ObjectManager::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $resourceMock->expects($this->once())->method('getId')->willReturn('1'); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->stateMachineMock->expects($this->once())->method('can')->with($configurationMock, $resourceMock)->willReturn(false); $this->stateMachineMock->expects($this->never())->method('apply')->with($configurationMock, $resourceMock); $objectManagerMock->expects($this->never())->method('flush'); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent'); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash'); $this->flashHelperMock->expects($this->never())->method('addFlashFromEvent'); $this->expectException(BadRequestHttpException::class); $this->resourceController->applyStateMachineTransitionAction($request); } public function testAppliesStateMachineTransitionToResourceAndRedirectsForHtmlRequest(): void { /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $postEventMock */ $postEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn('1'); $configurationMock->method('isHtmlRequest')->willReturn(true); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->stateMachineMock->expects($this->once())->method('can')->with($configurationMock, $resourceMock)->willReturn(true); $this->resourceUpdateHandlerMock->expects($this->once())->method('handle')->with($resourceMock, $configurationMock, $this->managerMock); $this->flashHelperMock->expects($this->once())->method('addSuccessFlash')->with($configurationMock, ResourceActions::UPDATE, $resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPostEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($postEventMock); $postEventMock->expects($this->once())->method('getResponse')->willReturn(null); $this->redirectHandlerMock->expects($this->once())->method('redirectToResource')->with($configurationMock, $resourceMock)->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->applyStateMachineTransitionAction($request)); } public function testUsesResponseFromPostApplyStateMachineTransitionEventIfDefined(): void { /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var ResourceControllerEvent|MockObject $postEventMock */ $postEventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn('1'); $configurationMock->method('isHtmlRequest')->willReturn(true); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->stateMachineMock->expects($this->once())->method('can')->with($configurationMock, $resourceMock)->willReturn(true); $this->resourceUpdateHandlerMock->expects($this->once())->method('handle')->with($resourceMock, $configurationMock, $this->managerMock); $this->flashHelperMock->expects($this->once())->method('addSuccessFlash')->with($configurationMock, ResourceActions::UPDATE, $resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPostEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($postEventMock); $postEventMock->expects($this->once())->method('getResponse')->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->applyStateMachineTransitionAction($request)); } public function testDoesNotApplyStateMachineTransitionOnResourceAndRedirectsForHtmlRequestsStoppedViaEvent(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $redirectResponseMock */ $redirectResponseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn('1'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(true); $this->managerMock->expects($this->never())->method('flush'); $this->stateMachineMock->expects($this->never())->method('apply')->with($resourceMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash')->with($configurationMock, ResourceActions::UPDATE, $resourceMock); $eventMock->expects($this->once())->method('getResponse')->willReturn(null); $this->flashHelperMock->expects($this->once())->method('addFlashFromEvent')->with($configurationMock, $eventMock); $this->redirectHandlerMock->expects($this->once())->method('redirectToResource')->with($configurationMock, $resourceMock)->willReturn($redirectResponseMock); $this->assertSame($redirectResponseMock, $this->resourceController->applyStateMachineTransitionAction($request)); } public function testDoesNotApplyStateMachineTransitionOnResourceAndReturnEventResponseForHtmlRequestsStoppedViaEvent(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var CsrfTokenManagerInterface|MockObject $csrfTokenManagerMock */ $csrfTokenManagerMock = $this->createMock(CsrfTokenManagerInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(request: ['_csrf_token' => 'xyz']); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(true); $this->containerMock->expects($this->once())->method('has')->with('security.csrf.token_manager')->willReturn(true); $this->containerMock->expects($this->once())->method('get')->with('security.csrf.token_manager')->willReturn($csrfTokenManagerMock); $csrfTokenManagerMock->expects($this->once())->method('isTokenValid')->with(new CsrfToken('1', 'xyz'))->willReturn(true); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $resourceMock->expects($this->once())->method('getId')->willReturn('1'); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(true); $this->managerMock->expects($this->never())->method('flush'); $this->stateMachineMock->expects($this->never())->method('apply')->with($resourceMock); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash')->with($configurationMock, ResourceActions::UPDATE, $resourceMock); $this->flashHelperMock->expects($this->once())->method('addFlashFromEvent')->with($configurationMock, $eventMock); $eventMock->expects($this->once())->method('getResponse')->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->applyStateMachineTransitionAction($request)); } public function testAppliesStateMachineTransitionOnResourceAndReturns200ForNonHtmlRequests(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); /** @var ParameterBagInterface|MockObject $parameterBagMock */ $parameterBagMock = $this->createMock(ParameterBagInterface::class); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('getParameters')->willReturn($parameterBagMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(false); $parameterBagMock->expects($this->once())->method('get')->with('return_content', true)->willReturn(true); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.update') ->willReturn(true); $this->singleResourceProviderMock ->expects($this->once()) ->method('get') ->with($configurationMock, $this->repositoryMock) ->willReturn($resourceMock); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPreEvent') ->with(ResourceActions::UPDATE, $configurationMock, $resourceMock) ->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->stateMachineMock ->expects($this->once()) ->method('can') ->with($configurationMock, $resourceMock) ->willReturn(true); $this->resourceUpdateHandlerMock ->expects($this->once()) ->method('handle') ->with($resourceMock, $configurationMock, $this->managerMock); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPostEvent') ->with(ResourceActions::UPDATE, $configurationMock, $resourceMock); $expectedView = View::create($resourceMock, 200); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->with( $this->anything(), $this->callback(function ($view) use ($expectedView) { return $view instanceof View && $view->getData() === $expectedView->getData() && $view->getStatusCode() === $expectedView->getStatusCode(); }), ) ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->applyStateMachineTransitionAction($request)); } public function testAppliesStateMachineTransitionOnResourceAndReturns204ForNonHtmlRequestsIfAdditionalOptionAdded(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); /** @var ParameterBagInterface|MockObject $parameterBagMock */ $parameterBagMock = $this->createMock(ParameterBagInterface::class); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $request = new Request(); $this->requestConfigurationFactoryMock ->expects($this->once()) ->method('create') ->with($this->metadataMock, $request) ->willReturn($configurationMock); $configurationMock->expects($this->once())->method('getParameters')->willReturn($parameterBagMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(false); $parameterBagMock->expects($this->once())->method('get')->with('return_content', true)->willReturn(false); $this->authorizationCheckerMock ->expects($this->once()) ->method('isGranted') ->with($configurationMock, 'sylius.product.update') ->willReturn(true); $this->singleResourceProviderMock ->expects($this->once()) ->method('get') ->with($configurationMock, $this->repositoryMock) ->willReturn($resourceMock); $configurationMock->method('isHtmlRequest')->willReturn(false); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPreEvent') ->with(ResourceActions::UPDATE, $configurationMock, $resourceMock) ->willReturn($eventMock); $eventMock->method('isStopped')->willReturn(false); $this->stateMachineMock ->expects($this->once()) ->method('can') ->with($configurationMock, $resourceMock) ->willReturn(true); $this->resourceUpdateHandlerMock ->expects($this->once()) ->method('handle') ->with($resourceMock, $configurationMock, $this->managerMock); $this->eventDispatcherMock ->expects($this->once()) ->method('dispatchPostEvent') ->with(ResourceActions::UPDATE, $configurationMock, $resourceMock); $expectedView = View::create(null, 204); $this->viewHandlerMock ->expects($this->once()) ->method('handle') ->with( $this->anything(), $this->callback(function ($view) use ($expectedView) { return $view instanceof View && $view->getData() === $expectedView->getData() && $view->getStatusCode() === $expectedView->getStatusCode(); }), ) ->willReturn($responseMock); $this->assertSame($responseMock, $this->resourceController->applyStateMachineTransitionAction($request)); } public function testDoesNotApplyStateMachineTransitionResourceAndThrowsHttpExceptionForNonHtmlRequestsStoppedViaEvent(): void { /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ObjectManager|MockObject $objectManagerMock */ $objectManagerMock = $this->createMock(ObjectManager::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceControllerEvent|MockObject $eventMock */ $eventMock = $this->createMock(ResourceControllerEvent::class); $request = new Request(); $this->requestConfigurationFactoryMock->expects($this->once())->method('create')->with($this->metadataMock, $request)->willReturn($configurationMock); $configurationMock->expects($this->once())->method('hasPermission')->willReturn(true); $configurationMock->expects($this->once())->method('getPermission')->with(ResourceActions::UPDATE)->willReturn('sylius.product.update'); $configurationMock->expects($this->once())->method('isCsrfProtectionEnabled')->willReturn(false); $configurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $this->authorizationCheckerMock->expects($this->once())->method('isGranted')->with($configurationMock, 'sylius.product.update')->willReturn(true); $this->singleResourceProviderMock->expects($this->once())->method('get')->with($configurationMock, $this->repositoryMock)->willReturn($resourceMock); $this->eventDispatcherMock->expects($this->once())->method('dispatchPreEvent')->with(ResourceActions::UPDATE, $configurationMock, $resourceMock)->willReturn($eventMock); $eventMock->expects($this->once())->method('isStopped')->willReturn(true); $eventMock->expects($this->once())->method('getMessage')->willReturn('Cannot approve this product.'); $eventMock->expects($this->once())->method('getErrorCode')->willReturn(500); $this->stateMachineMock->expects($this->never())->method('apply')->with($configurationMock, $resourceMock); $objectManagerMock->expects($this->never())->method('flush'); $this->eventDispatcherMock->expects($this->never())->method('dispatchPostEvent'); $this->flashHelperMock->expects($this->never())->method('addSuccessFlash'); $this->flashHelperMock->expects($this->never())->method('addFlashFromEvent'); $this->expectException(HttpException::class); $this->resourceController->applyStateMachineTransitionAction($request); } private function markAsSkippedIfFosRestBundleIsNotAvailable(): void { if (!class_exists(FOSRestBundle::class)) { $this->markTestSkipped('FriendsOfSymfony Rest Bundle is not installed.'); } } } ================================================ FILE: tests/Bundle/Controller/ResourceDeleteHandlerTest.php ================================================ resourceDeleteHandler = new ResourceDeleteHandler(); } public function testImplementsAResourceDeleteHandlerInterface(): void { $this->assertInstanceOf(ResourceDeleteHandlerInterface::class, $this->resourceDeleteHandler); } public function testRemovesResourceViaRepository(): void { /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); $repositoryMock->expects($this->once())->method('remove')->with($resourceMock); $this->resourceDeleteHandler->handle($resourceMock, $repositoryMock); } } ================================================ FILE: tests/Bundle/Controller/ResourceFormFactoryTest.php ================================================ formFactoryMock = $this->createMock(FormFactoryInterface::class); $this->resourceFormFactory = new ResourceFormFactory($this->formFactoryMock); } public function testImplementsResourceFormFactoryInterface(): void { $this->assertInstanceOf(ResourceFormFactoryInterface::class, $this->resourceFormFactory); } public function testCreatesAppropriateFormBasedOnConfiguration(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var FormInterface|MockObject $formMock */ $formMock = $this->createMock(FormInterface::class); $requestConfigurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $requestConfigurationMock->expects($this->once())->method('getFormType')->willReturn('sylius_product_pricing'); $requestConfigurationMock->expects($this->once())->method('getFormOptions')->willReturn([]); $this->formFactoryMock->expects($this->once())->method('create')->with('sylius_product_pricing', $resourceMock, $this->isType('array'))->willReturn($formMock); $this->assertSame($formMock, $this->resourceFormFactory->create($requestConfigurationMock, $resourceMock)); } public function testCreatesFormWithoutRootNameAndDisablesCsrfProtectionForNonHtmlRequests(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var FormInterface|MockObject $formMock */ $formMock = $this->createMock(FormInterface::class); $requestConfigurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $requestConfigurationMock->expects($this->once())->method('getFormType')->willReturn('sylius_product_api'); $requestConfigurationMock->expects($this->once())->method('getFormOptions')->willReturn([]); $this->formFactoryMock->expects($this->once())->method('createNamed')->with('', 'sylius_product_api', $resourceMock, ['csrf_protection' => false])->willReturn($formMock); $this->assertSame($formMock, $this->resourceFormFactory->create($requestConfigurationMock, $resourceMock)); } } ================================================ FILE: tests/Bundle/Controller/ResourceUpdateHandlerTest.php ================================================ stateMachineMock = $this->createMock(StateMachineInterface::class); $this->resourceUpdateHandler = new ResourceUpdateHandler($this->stateMachineMock); } public function testImplementsAResourceUpdateHandlerInterface(): void { $this->assertInstanceOf(ResourceUpdateHandlerInterface::class, $this->resourceUpdateHandler); } public function testAppliesAStateMachineTransition(): void { /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ObjectManager|MockObject $managerMock */ $managerMock = $this->createMock(ObjectManager::class); $configurationMock->expects($this->once())->method('hasStateMachine')->willReturn(true); $this->stateMachineMock->expects($this->once())->method('apply')->with($configurationMock, $resourceMock); $managerMock->expects($this->once())->method('flush'); $this->resourceUpdateHandler->handle($resourceMock, $configurationMock, $managerMock); } public function testDoesNotApplyAStateMachineTransition(): void { /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); /** @var RequestConfiguration|MockObject $configurationMock */ $configurationMock = $this->createMock(RequestConfiguration::class); /** @var ObjectManager|MockObject $managerMock */ $managerMock = $this->createMock(ObjectManager::class); $configurationMock->expects($this->once())->method('hasStateMachine')->willReturn(false); $this->stateMachineMock->expects($this->never())->method('apply')->with($configurationMock, $resourceMock); $managerMock->expects($this->once())->method('flush'); $this->resourceUpdateHandler->handle($resourceMock, $configurationMock, $managerMock); } } ================================================ FILE: tests/Bundle/Controller/ResourcesCollectionProviderTest.php ================================================ resourcesResolverMock = $this->createMock(ResourcesResolverInterface::class); $this->resourcesCollectionProvider = new ResourcesCollectionProvider($this->resourcesResolverMock, null); } public function testImplementsResourcesCollectionProviderInterface(): void { $this->assertInstanceOf(ResourcesCollectionProviderInterface::class, $this->resourcesCollectionProvider); } public function testReturnsResourcesResolvedFromRepository(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $firstResourceMock */ $firstResourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceInterface|MockObject $secondResourceMock */ $secondResourceMock = $this->createMock(ResourceInterface::class); $this->resourcesResolverMock->expects($this->once())->method('getResources')->with($requestConfigurationMock, $repositoryMock)->willReturn([$firstResourceMock, $secondResourceMock]); $this->assertSame([$firstResourceMock, $secondResourceMock], $this->resourcesCollectionProvider->get($requestConfigurationMock, $repositoryMock)); } public function testHandlesPagerfanta(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var \Pagerfanta\Pagerfanta|MockObject $paginatorMock */ $paginatorMock = $this->createMock(Pagerfanta::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); $queryParameters = new InputBag(); $queryParameters->set('limit', 5); $queryParameters->set('page', 6); $requestConfigurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $requestConfigurationMock->expects($this->once())->method('getPaginationMaxPerPage')->willReturn(5); $this->resourcesResolverMock->expects($this->once())->method('getResources')->with($requestConfigurationMock, $repositoryMock)->willReturn($paginatorMock); $requestConfigurationMock->expects($this->once())->method('getRequest')->willReturn($requestMock); $requestMock->query = $queryParameters; $this->assertEquals(5, $requestMock->query->get('limit')); $this->assertEquals(6, $requestMock->query->get('page')); $paginatorMock->expects($this->once())->method('setMaxPerPage')->with(5); $paginatorMock->expects($this->once())->method('setCurrentPage')->with(6); $paginatorMock->expects($this->once())->method('getCurrentPageResults')->willReturn([]); $this->assertSame($paginatorMock, $this->resourcesCollectionProvider->get($requestConfigurationMock, $repositoryMock)); } public function testRestrictsMaxPaginationLimitBasedOnGridConfiguration(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceGridView|MockObject $gridViewMock */ $gridViewMock = $this->createMock(ResourceGridView::class); /** @var Grid|MockObject $gridMock */ $gridMock = $this->createMock(Grid::class); /** @var \Pagerfanta\Pagerfanta|MockObject $paginatorMock */ $paginatorMock = $this->createMock(Pagerfanta::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); $queryParameters = new InputBag(); $queryParameters->set('limit', 1000); $queryParameters->set('page', 1); $requestConfigurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $requestConfigurationMock->expects($this->once())->method('getPaginationMaxPerPage')->willReturn(1000); $gridMock->expects($this->once())->method('getLimits')->willReturn([10, 20, 99]); $gridViewMock->expects($this->once())->method('getDefinition')->willReturn($gridMock); $gridViewMock->expects($this->once())->method('getData')->willReturn($paginatorMock); $this->resourcesResolverMock->expects($this->once())->method('getResources')->with($requestConfigurationMock, $repositoryMock)->willReturn($gridViewMock); $requestConfigurationMock->expects($this->once())->method('getRequest')->willReturn($requestMock); $requestMock->query = $queryParameters; $this->assertEquals(1000, $requestMock->query->get('limit')); $this->assertEquals(1, $requestMock->query->get('page')); $paginatorMock->expects($this->once())->method('setMaxPerPage')->with(99); $paginatorMock->expects($this->once())->method('setCurrentPage')->with(1); $paginatorMock->expects($this->once())->method('getCurrentPageResults')->willReturn([]); $this->assertSame($gridViewMock, $this->resourcesCollectionProvider->get($requestConfigurationMock, $repositoryMock)); } public function testCreatesAPaginatedRepresentationForPagerfantaForNonHtmlRequests(): void { /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var MetadataInterface|MockObject $metadataMock */ $metadataMock = $this->createMock(MetadataInterface::class); if (!class_exists(PagerfantaFactory::class)) { $this->markTestSkipped('PagerfantaFactory is not installed.'); } $this->resourcesCollectionProvider = new ResourcesCollectionProvider(new ResourcesResolver(), new PagerfantaFactory()); $paginator = new Pagerfanta(new ArrayAdapter([])); $repositoryMock->expects($this->once())->method('createPaginator')->with([], [])->willReturn($paginator); $request = new Request(); $request->query = new InputBag(['limit' => 8, 'page' => 1]); $request->attributes = new ParameterBag(['_format' => 'json', '_route' => 'sylius_product_index', '_route_params' => ['slug' => 'foo-bar']]); $requestConfiguration = new RequestConfiguration($metadataMock, $request, new Parameters(['paginate' => true])); $this->assertInstanceOf(PaginatedRepresentation::class, $this->resourcesCollectionProvider->get($requestConfiguration, $repositoryMock)); } public function testHandlesResourceGridView(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceGridView|MockObject $resourceGridViewMock */ $resourceGridViewMock = $this->createMock(ResourceGridView::class); /** @var Grid|MockObject $gridMock */ $gridMock = $this->createMock(Grid::class); /** @var \Pagerfanta\Pagerfanta|MockObject $paginatorMock */ $paginatorMock = $this->createMock(Pagerfanta::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); $queryParameters = new InputBag(); $queryParameters->set('limit', 5); $queryParameters->set('page', 6); $requestConfigurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $requestConfigurationMock->expects($this->once())->method('getPaginationMaxPerPage')->willReturn(5); $this->resourcesResolverMock->expects($this->once())->method('getResources')->with($requestConfigurationMock, $repositoryMock)->willReturn($resourceGridViewMock); $resourceGridViewMock->expects($this->once())->method('getData')->willReturn($paginatorMock); $gridMock->expects($this->once())->method('getLimits')->willReturn([10, 25, 50]); $resourceGridViewMock->expects($this->once())->method('getDefinition')->willReturn($gridMock); $requestConfigurationMock->expects($this->once())->method('getRequest')->willReturn($requestMock); $requestMock->query = $queryParameters; $this->assertEquals(5, $requestMock->query->get('limit')); $this->assertEquals(6, $requestMock->query->get('page')); $paginatorMock->expects($this->once())->method('setMaxPerPage')->with(5); $paginatorMock->expects($this->once())->method('setCurrentPage')->with(6); $paginatorMock->expects($this->once())->method('getCurrentPageResults')->willReturn([]); $this->assertSame($resourceGridViewMock, $this->resourcesCollectionProvider->get($requestConfigurationMock, $repositoryMock)); } } ================================================ FILE: tests/Bundle/Controller/ResourcesResolverTest.php ================================================ resourcesResolver = new ResourcesResolver(); } public function testImplementsResourcesResolverInterface(): void { $this->assertInstanceOf(ResourcesResolverInterface::class, $this->resourcesResolver); } public function testGetsAllResourcesIfHasNoCriteria(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $firstResourceMock */ $firstResourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceInterface|MockObject $secondResourceMock */ $secondResourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn(null); $requestConfigurationMock->expects($this->once())->method('isPaginated')->willReturn(false); $requestConfigurationMock->expects($this->once())->method('isFilterable')->willReturn(false); $requestConfigurationMock->expects($this->once())->method('isSortable')->willReturn(false); $requestConfigurationMock->expects($this->once())->method('getLimit')->willReturn(null); $repositoryMock->expects($this->once())->method('findBy')->with([], [], null)->willReturn([$firstResourceMock, $secondResourceMock]); $this->assertSame([$firstResourceMock, $secondResourceMock], $this->resourcesResolver->getResources($requestConfigurationMock, $repositoryMock)); } public function testFindsResourcesByCriteriaIfNotPaginated(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $firstResourceMock */ $firstResourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceInterface|MockObject $secondResourceMock */ $secondResourceMock = $this->createMock(ResourceInterface::class); /** @var ResourceInterface|MockObject $thirdResourceMock */ $thirdResourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn(null); $requestConfigurationMock->expects($this->once())->method('isPaginated')->willReturn(false); $requestConfigurationMock->expects($this->once())->method('isFilterable')->willReturn(true); $requestConfigurationMock->expects($this->once())->method('isSortable')->willReturn(true); $requestConfigurationMock->expects($this->once())->method('getLimit')->willReturn(15); $requestConfigurationMock->expects($this->once())->method('getCriteria')->willReturn(['custom' => 'criteria']); $requestConfigurationMock->expects($this->once())->method('getSorting')->willReturn(['name' => 'desc']); $repositoryMock->expects($this->once())->method('findBy')->with(['custom' => 'criteria'], ['name' => 'desc'], 15)->willReturn([$firstResourceMock, $secondResourceMock, $thirdResourceMock]); $this->assertSame([$firstResourceMock, $secondResourceMock, $thirdResourceMock], $this->resourcesResolver->getResources($requestConfigurationMock, $repositoryMock)); } public function testUsesCustomMethodAndArgumentsIfSpecified(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $firstResourceMock */ $firstResourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn('findAll'); $requestConfigurationMock->expects($this->once())->method('getRepositoryArguments')->willReturn(['foo']); $repositoryMock->expects($this->once())->method('findAll')->with('foo')->willReturn([$firstResourceMock]); $this->assertSame([$firstResourceMock], $this->resourcesResolver->getResources($requestConfigurationMock, $repositoryMock)); } public function testUsesCustomRepositoryIfSpecified(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var RepositoryInterface|MockObject $customRepositoryMock */ $customRepositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $firstResourceMock */ $firstResourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn([$customRepositoryMock, 'findBy']); $requestConfigurationMock->expects($this->once())->method('getRepositoryArguments')->willReturn([['foo' => true]]); $customRepositoryMock->expects($this->once())->method('findBy')->with(['foo' => true])->willReturn([$firstResourceMock]); $this->assertSame([$firstResourceMock], $this->resourcesResolver->getResources($requestConfigurationMock, $repositoryMock)); } public function testCreatesPaginatorByDefault(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var \Pagerfanta\Pagerfanta|MockObject $paginatorMock */ $paginatorMock = $this->createMock(Pagerfanta::class); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn(null); $requestConfigurationMock->expects($this->once())->method('isPaginated')->willReturn(true); $requestConfigurationMock->expects($this->once())->method('isFilterable')->willReturn(false); $requestConfigurationMock->expects($this->once())->method('isSortable')->willReturn(false); $repositoryMock->expects($this->once())->method('createPaginator')->with([], [])->willReturn($paginatorMock); $this->assertSame($paginatorMock, $this->resourcesResolver->getResources($requestConfigurationMock, $repositoryMock)); } } ================================================ FILE: tests/Bundle/Controller/SingleResourceProviderTest.php ================================================ singleResourceProvider = new SingleResourceProvider(); } public function testImplementsSingleResourceProviderInterface(): void { $this->assertInstanceOf(SingleResourceProviderInterface::class, $this->singleResourceProvider); } public function testLooksForSpecificResourceWithIdByDefault(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var ParameterBag|MockObject $requestAttributesMock */ $requestAttributesMock = $this->createMock(ParameterBag::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn(null); $requestConfigurationMock->expects($this->once())->method('getRequest')->willReturn($requestMock); $requestMock->attributes = $requestAttributesMock; $requestAttributesMock->method('has')->with('id')->willReturn(true); $requestAttributesMock->expects($this->once())->method('get')->with('id')->willReturn(5); $repositoryMock->expects($this->once())->method('find')->with(5)->willReturn(null); $this->assertNull($this->singleResourceProvider->get($requestConfigurationMock, $repositoryMock)); } public function testCanFindSpecificResourceWithIdByDefault(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var ParameterBag|MockObject $requestAttributesMock */ $requestAttributesMock = $this->createMock(ParameterBag::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn(null); $requestConfigurationMock->expects($this->once())->method('getRequest')->willReturn($requestMock); $requestMock->attributes = $requestAttributesMock; $requestAttributesMock->method('has')->with('id')->willReturn(true); $requestAttributesMock->expects($this->once())->method('get')->with('id')->willReturn(3); $repositoryMock->expects($this->once())->method('find')->with(3)->willReturn($resourceMock); $this->assertSame($resourceMock, $this->singleResourceProvider->get($requestConfigurationMock, $repositoryMock)); } public function testCanFindSpecificResourceWithSlugByDefault(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var ParameterBag|MockObject $requestAttributesMock */ $requestAttributesMock = $this->createMock(ParameterBag::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getCriteria')->willReturn([]); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn(null); $requestConfigurationMock->expects($this->once())->method('getRequest')->willReturn($requestMock); $requestMock->attributes = $requestAttributesMock; $requestAttributesMock->expects($this->exactly(2))->method('has')->willReturnMap([['id', false], ['slug', true]]); $requestAttributesMock->expects($this->once())->method('get')->with('slug')->willReturn('the-most-awesome-hat'); $repositoryMock->expects($this->once())->method('findOneBy')->with(['slug' => 'the-most-awesome-hat'])->willReturn($resourceMock); $this->assertSame($resourceMock, $this->singleResourceProvider->get($requestConfigurationMock, $repositoryMock)); } public function testCanFindSpecificResourceWithCustomCriteria(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var ParameterBag|MockObject $requestAttributesMock */ $requestAttributesMock = $this->createMock(ParameterBag::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getCriteria')->willReturn(['request-configuration-criteria' => '1']); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn(null); $requestConfigurationMock->expects($this->once())->method('getRequest')->willReturn($requestMock); $requestMock->attributes = $requestAttributesMock; $requestAttributesMock->expects($this->exactly(2))->method('has')->willReturnMap([['id', false], ['slug', false]]); $repositoryMock->expects($this->once())->method('findOneBy')->with(['request-configuration-criteria' => '1'])->willReturn($resourceMock); $this->assertSame($resourceMock, $this->singleResourceProvider->get($requestConfigurationMock, $repositoryMock)); } public function testCanFindSpecificResourceWithMergedCustomCriteria(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var ParameterBag|MockObject $requestAttributesMock */ $requestAttributesMock = $this->createMock(ParameterBag::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getCriteria')->willReturn(['request-configuration-criteria' => '1']); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn(null); $requestConfigurationMock->expects($this->once())->method('getRequest')->willReturn($requestMock); $requestMock->attributes = $requestAttributesMock; $requestAttributesMock->expects($this->exactly(2))->method('has')->willReturnMap([['id', false], ['slug', true]]); $requestAttributesMock->expects($this->once())->method('get')->with('slug')->willReturn('banana'); $repositoryMock->expects($this->once())->method('findOneBy')->with(['slug' => 'banana', 'request-configuration-criteria' => '1'])->willReturn($resourceMock); $this->assertSame($resourceMock, $this->singleResourceProvider->get($requestConfigurationMock, $repositoryMock)); } public function testCanFindSpecificResourceWithMergedCustomCriteriaOverwritingTheAttributes(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var Request|MockObject $requestMock */ $requestMock = $this->createMock(Request::class); /** @var ParameterBag|MockObject $requestAttributesMock */ $requestAttributesMock = $this->createMock(ParameterBag::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getCriteria')->willReturn(['id' => 5]); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn(null); $requestConfigurationMock->expects($this->once())->method('getRequest')->willReturn($requestMock); $requestMock->attributes = $requestAttributesMock; $requestAttributesMock->expects($this->exactly(2))->method('has')->willReturnMap([['id', false], ['slug', false]]); $repositoryMock->expects($this->once())->method('findOneBy')->with(['id' => 5])->willReturn($resourceMock); $this->assertSame($resourceMock, $this->singleResourceProvider->get($requestConfigurationMock, $repositoryMock)); } public function testUsesACustomMethodIfConfigured(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn('find'); $requestConfigurationMock->expects($this->once())->method('getRepositoryArguments')->willReturn(['foo']); $repositoryMock->expects($this->once())->method('find')->with('foo')->willReturn($resourceMock); $this->assertSame($resourceMock, $this->singleResourceProvider->get($requestConfigurationMock, $repositoryMock)); } public function testUsesACustomRepositoryIfConfigured(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var RepositoryInterface|MockObject $repositoryMock */ $repositoryMock = $this->createMock(RepositoryInterface::class); /** @var RepositoryInterface|MockObject $customRepositoryMock */ $customRepositoryMock = $this->createMock(RepositoryInterface::class); /** @var ResourceInterface|MockObject $resourceMock */ $resourceMock = $this->createMock(ResourceInterface::class); $requestConfigurationMock->expects($this->once())->method('getRepositoryMethod')->willReturn([$customRepositoryMock, 'find']); $requestConfigurationMock->expects($this->once())->method('getRepositoryArguments')->willReturn(['foo']); $customRepositoryMock->expects($this->once())->method('find')->with('foo')->willReturn($resourceMock); $this->assertSame($resourceMock, $this->singleResourceProvider->get($requestConfigurationMock, $repositoryMock)); } } ================================================ FILE: tests/Bundle/Controller/StateMachineTest.php ================================================ markAsSkippedIfWinzouStateMachineIsNotAvailable(); $this->stateMachineFactoryMock = $this->createMock(FactoryInterface::class); $this->stateMachine = new StateMachine($this->stateMachineFactoryMock); } public function testImplementsStateMachineInterface(): void { $this->assertInstanceOf(ResourceStateMachineInterface::class, $this->stateMachine); } public function testThrowsAnExceptionIfTransitionIsNotDefinedDuringCan(): void { $requestConfiguration = $this->createRequestConfigurationWithoutStateMachine(); $resource = $this->createMock(ResourceInterface::class); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('State machine must be configured to apply transition, check your routing.'); $this->stateMachine->can($requestConfiguration, $resource); } public function testThrowsAnExceptionIfTransitionIsNotDefinedDuringApply(): void { $requestConfiguration = $this->createRequestConfigurationWithoutStateMachine(); $resource = $this->createMock(ResourceInterface::class); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('State machine must be configured to apply transition, check your routing.'); $this->stateMachine->apply($requestConfiguration, $resource); } public function testReturnsIfConfiguredStateMachineCanTransition(): void { $requestConfiguration = $this->createRequestConfiguration('sylius_product_review_state', 'reject'); $resource = $this->createMock(ResourceInterface::class); $stateMachineMock = $this->createStateMachineMock(); $this->stateMachineFactoryMock ->expects($this->once()) ->method('get') ->with($resource, 'sylius_product_review_state') ->willReturn($stateMachineMock); $stateMachineMock ->expects($this->once()) ->method('can') ->with('reject') ->willReturn(true); $this->assertTrue($this->stateMachine->can($requestConfiguration, $resource)); } public function testAppliesConfiguredStateMachineTransitionWithoutGraphConfiguration(): void { $requestConfiguration = $this->createRequestConfiguration(null, 'reject'); $resource = $this->createMock(ResourceInterface::class); $stateMachineMock = $this->createStateMachineMock(); $this->stateMachineFactoryMock ->expects($this->once()) ->method('get') ->with($resource, 'default') ->willReturn($stateMachineMock); $stateMachineMock ->expects($this->once()) ->method('apply') ->with('reject'); $this->stateMachine->apply($requestConfiguration, $resource); } public function testAppliesConfiguredStateMachineTransitionWithGraphConfiguration(): void { $requestConfiguration = $this->createRequestConfiguration('sylius_product_review_state', 'reject'); $resource = $this->createMock(ResourceInterface::class); $stateMachineMock = $this->createStateMachineMock(); $this->stateMachineFactoryMock ->expects($this->once()) ->method('get') ->with($resource, 'sylius_product_review_state') ->willReturn($stateMachineMock); $stateMachineMock ->expects($this->once()) ->method('apply') ->with('reject'); $this->stateMachine->apply($requestConfiguration, $resource); } /** * @return MockObject&RequestConfiguration */ private function createRequestConfigurationWithoutStateMachine(): MockObject { /** @var MockObject&RequestConfiguration $requestConfiguration */ $requestConfiguration = $this->createMock(RequestConfiguration::class); $requestConfiguration->method('hasStateMachine')->willReturn(false); return $requestConfiguration; } /** * @return MockObject&RequestConfiguration */ private function createRequestConfiguration(?string $graph, string $transition): MockObject { /** @var MockObject&RequestConfiguration $requestConfiguration */ $requestConfiguration = $this->createMock(RequestConfiguration::class); $requestConfiguration->method('hasStateMachine')->willReturn(true); $requestConfiguration->method('getStateMachineGraph')->willReturn($graph); $requestConfiguration->method('getStateMachineTransition')->willReturn($transition); return $requestConfiguration; } /** * @return MockObject&StateMachineInterface */ private function createStateMachineMock(): MockObject { /** @var MockObject&StateMachineInterface $stateMachine */ $stateMachine = $this->createMock(StateMachineInterface::class); return $stateMachine; } private function markAsSkippedIfWinzouStateMachineIsNotAvailable(): void { if (!class_exists(winzouStateMachineBundle::class)) { $this->markTestSkipped('Winzou State machine is not available.'); } } } ================================================ FILE: tests/Bundle/Controller/ViewHandlerTest.php ================================================ markAsSkippedIfFosRestBundleIsNotAvailable(); $this->restViewHandlerMock = $this->createMock(ConfigurableViewHandlerInterface::class); $this->viewHandler = new ViewHandler($this->restViewHandlerMock); } public function testImplementsViewHandlerInterface(): void { $this->assertInstanceOf(ViewHandlerInterface::class, $this->viewHandler); } public function testHandlesViewNormallyForHtmlRequests(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $requestConfigurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(true); $view = View::create(); $this->restViewHandlerMock->expects($this->once())->method('handle')->with($view)->willReturn($responseMock); $this->assertSame($responseMock, $this->viewHandler->handle($requestConfigurationMock, $view)); } public function testSetsProperValuesForNonHtmlRequests(): void { /** @var RequestConfiguration|MockObject $requestConfigurationMock */ $requestConfigurationMock = $this->createMock(RequestConfiguration::class); /** @var Response|MockObject $responseMock */ $responseMock = $this->createMock(Response::class); $requestConfigurationMock->expects($this->once())->method('isHtmlRequest')->willReturn(false); $view = View::create(); $view->setContext(new Context()); $requestConfigurationMock->expects($this->once())->method('getSerializationGroups')->willReturn(['Detailed']); $requestConfigurationMock->expects($this->once())->method('getSerializationVersion')->willReturn('2.0.0'); $this->restViewHandlerMock->expects($this->once())->method('setExclusionStrategyGroups')->with(['Detailed']); $this->restViewHandlerMock->expects($this->once())->method('setExclusionStrategyVersion')->with('2.0.0'); $this->restViewHandlerMock->expects($this->once())->method('handle')->with($view)->willReturn($responseMock); $this->assertSame($responseMock, $this->viewHandler->handle($requestConfigurationMock, $view)); } private function markAsSkippedIfFosRestBundleIsNotAvailable(): void { if (!class_exists(FOSRestBundle::class)) { $this->markTestSkipped('FriendsOfSymfony Rest Bundle is not installed.'); } } } ================================================ FILE: tests/Bundle/Controller/WorkflowTest.php ================================================ markAsSkippedIfSymfonyWorkflowIsNotAvailable(); $this->registryMock = $this->createMock(Registry::class); $this->workflow = new Workflow($this->registryMock); } public function testImplementsStateMachineInterface(): void { $this->assertInstanceOf(ResourceStateMachineInterface::class, $this->workflow); } public function testThrowsAnExceptionIfTransitionIsNotDefinedDuringCan(): void { $requestConfiguration = $this->createRequestConfigurationWithoutStateMachine(); $resource = $this->createMock(ResourceInterface::class); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('State machine must be configured to apply transition, check your routing.'); $this->workflow->can($requestConfiguration, $resource); } public function testThrowsAnExceptionIfTransitionIsNotDefinedDuringApply(): void { $requestConfiguration = $this->createRequestConfigurationWithoutStateMachine(); $resource = $this->createMock(ResourceInterface::class); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('State machine must be configured to apply transition, check your routing.'); $this->workflow->apply($requestConfiguration, $resource); } public function testReturnsIfConfiguredStateMachineCanTransitionWithoutGraphConfiguration(): void { $requestConfiguration = $this->createRequestConfiguration(null, 'reject'); $resource = $this->createMock(ResourceInterface::class); $workflowMock = $this->createWorkflowMock(); $this->registryMock ->expects($this->once()) ->method('get') ->with($resource, null) ->willReturn($workflowMock); $workflowMock ->expects($this->once()) ->method('can') ->with($resource, 'reject') ->willReturn(true); $this->assertTrue($this->workflow->can($requestConfiguration, $resource)); } public function testReturnsIfConfiguredStateMachineCanTransitionWithGraphConfiguration(): void { $requestConfiguration = $this->createRequestConfiguration('pull_request', 'reject'); $resource = $this->createMock(ResourceInterface::class); $workflowMock = $this->createWorkflowMock(); $this->registryMock ->expects($this->once()) ->method('get') ->with($resource, 'pull_request') ->willReturn($workflowMock); $workflowMock ->expects($this->once()) ->method('can') ->with($resource, 'reject') ->willReturn(true); $this->assertTrue($this->workflow->can($requestConfiguration, $resource)); } public function testAppliesConfiguredStateMachineTransitionWithoutGraphConfiguration(): void { $requestConfiguration = $this->createRequestConfiguration(null, 'reject'); $resource = $this->createMock(ResourceInterface::class); $workflowMock = $this->createWorkflowMock(); $marking = $this->createMock(Marking::class); $this->registryMock ->expects($this->once()) ->method('get') ->with($resource, null) ->willReturn($workflowMock); $workflowMock ->expects($this->once()) ->method('apply') ->with($resource, 'reject') ->willReturn($marking); $this->workflow->apply($requestConfiguration, $resource); } public function testAppliesConfiguredStateMachineTransitionWithGraphConfiguration(): void { $requestConfiguration = $this->createRequestConfiguration('pull_request', 'reject'); $resource = $this->createMock(ResourceInterface::class); $workflowMock = $this->createWorkflowMock(); $marking = $this->createMock(Marking::class); $this->registryMock ->expects($this->once()) ->method('get') ->with($resource, 'pull_request') ->willReturn($workflowMock); $workflowMock ->expects($this->once()) ->method('apply') ->with($resource, 'reject') ->willReturn($marking); $this->workflow->apply($requestConfiguration, $resource); } /** * @return MockObject&RequestConfiguration */ private function createRequestConfigurationWithoutStateMachine(): MockObject { /** @var MockObject&RequestConfiguration $requestConfiguration */ $requestConfiguration = $this->createMock(RequestConfiguration::class); $requestConfiguration->method('hasStateMachine')->willReturn(false); return $requestConfiguration; } /** * @return MockObject&RequestConfiguration */ private function createRequestConfiguration(?string $graph, string $transition): MockObject { /** @var MockObject&RequestConfiguration $requestConfiguration */ $requestConfiguration = $this->createMock(RequestConfiguration::class); $requestConfiguration->method('hasStateMachine')->willReturn(true); $requestConfiguration->method('getStateMachineGraph')->willReturn($graph); $requestConfiguration->method('getStateMachineTransition')->willReturn($transition); return $requestConfiguration; } /** * @return MockObject&SymfonyWorkflow */ private function createWorkflowMock(): MockObject { /** @var MockObject&SymfonyWorkflow $workflow */ $workflow = $this->createMock(SymfonyWorkflow::class); return $workflow; } private function markAsSkippedIfSymfonyWorkflowIsNotAvailable(): void { if (!class_exists(Registry::class)) { $this->markTestSkipped('Symfony Workflow is not available.'); } } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/DoctrineTargetEntitiesResolverPassTest.php ================================================ setDefinition('doctrine.orm.listeners.resolve_target_entity', new Definition()); $this->setParameter( 'sylius.resources', ['app.loremipsum' => ['classes' => ['model' => \stdClass::class, 'interface' => \Countable::class]]], ); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'doctrine.orm.listeners.resolve_target_entity', 'addResolveTargetEntity', [\Countable::class, \stdClass::class, []], ); } /** * @test */ public function it_adds_doctrine_event_listener_tag_to_target_entities_resolver_if_not_exists(): void { $this->setDefinition('doctrine.orm.listeners.resolve_target_entity', new Definition()); $this->setParameter('sylius.resources', []); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithTag( 'doctrine.orm.listeners.resolve_target_entity', 'doctrine.event_listener', ['event' => 'loadClassMetadata'], ); $this->assertContainerBuilderHasServiceDefinitionWithTag( 'doctrine.orm.listeners.resolve_target_entity', 'doctrine.event_listener', ['event' => 'onClassMetadataNotFound'], ); } protected function registerCompilerPass(ContainerBuilder $container): void { $targetEntitiesResolver = new class() implements TargetEntitiesResolverInterface { public function resolve(array $resourcesConfiguration): array { if ($resourcesConfiguration === ['app.loremipsum' => ['classes' => ['model' => \stdClass::class, 'interface' => \Countable::class]]]) { return [\Countable::class => \stdClass::class]; } return []; } }; $container->addCompilerPass(new DoctrineTargetEntitiesResolverPass($targetEntitiesResolver)); } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/Helper/TargetEntitiesResolverTest.php ================================================ resolver = new TargetEntitiesResolver(); } public function testItIsATargetEntitiesResolver(): void { $this->assertInstanceOf(TargetEntitiesResolverInterface::class, $this->resolver); } public function testItSkipsResourceInterface(): void { $emptyConfig = ['app.resource' => ['classes' => ['model' => Resource::class]]]; $this->assertSame([], $this->resolver->resolve($emptyConfig)); } public function testItAutodiscoversInterfacesBasedOnTheModelClass(): void { $flyConfig = ['app.fly' => ['classes' => ['model' => Fly::class]]]; $resolved = $this->resolver->resolve($flyConfig); $this->assertCount(2, $resolved); $this->assertSame(Fly::class, $resolved[FlyInterface::class]); $this->assertSame(Fly::class, $resolved[AnimalInterface::class]); $bearConfig = ['app.bear' => ['classes' => ['model' => Bear::class]]]; $resolved = $this->resolver->resolve($bearConfig); $this->assertCount(3, $resolved); $this->assertSame(Bear::class, $resolved[BearInterface::class]); $this->assertSame(Bear::class, $resolved[MammalInterface::class]); $this->assertSame(Bear::class, $resolved[AnimalInterface::class]); } public function testItAutodiscoversOnlyUniqueInterfacesBasedOnModelClasses(): void { $config = [ 'app.fly' => ['classes' => ['model' => Fly::class]], 'app.bear' => ['classes' => ['model' => Bear::class]], ]; $resolved = $this->resolver->resolve($config); $this->assertCount(3, $resolved); $this->assertSame(Bear::class, $resolved[BearInterface::class]); $this->assertSame(Bear::class, $resolved[MammalInterface::class]); $this->assertSame(Fly::class, $resolved[FlyInterface::class]); $this->assertArrayNotHasKey(AnimalInterface::class, $resolved); } public function testItAutodiscoversInterfacesOnModelsWhenPassedMultipleTimes(): void { $config = [ 'app.fly' => ['classes' => ['model' => Fly::class]], 'app.another_resource_with_fly_model' => ['classes' => ['model' => Fly::class]], ]; $resolved = $this->resolver->resolve($config); $this->assertCount(2, $resolved); $this->assertSame(Fly::class, $resolved[FlyInterface::class]); $this->assertSame(Fly::class, $resolved[AnimalInterface::class]); } public function testItUsesTheInterfaceDefinedInTheConfig(): void { $config = [ 'app.deprecated' => ['classes' => ['model' => Resource::class, 'interface' => \Countable::class]], ]; $resolved = @$this->resolver->resolve($config); $this->assertCount(1, $resolved); $this->assertSame(Resource::class, $resolved[\Countable::class]); } public function testItUsesTheInterfaceDefinedExplicitlyOverTheAutodiscoveredOne(): void { $config = [ 'app.deprecated' => ['classes' => ['model' => Resource::class, 'interface' => MammalInterface::class]], 'app.bear' => ['classes' => ['model' => Bear::class]], ]; $resolved = @$this->resolver->resolve($config); $this->assertCount(3, $resolved); $this->assertSame(Resource::class, $resolved[MammalInterface::class]); $this->assertSame(Bear::class, $resolved[AnimalInterface::class]); $this->assertSame(Bear::class, $resolved[BearInterface::class]); } public function testItThrowsAnExceptionIfModelClassCannotBeResolved(): void { $config = ['app.error' => ['classes' => ['interface' => \Countable::class]]]; $this->expectException(\InvalidArgumentException::class); $this->resolver->resolve($config); } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/PagerfantaBridgePassTest.php ================================================ registerService('pagerfanta.twig_extension', PagerfantaExtension::class); $this->registerService('pagerfanta.view_factory', ViewFactory::class); $this->setParameter('white_october_pagerfanta.view_factory.class', 'My\ViewFactory'); $this->compile(); $this->assertContainerBuilderHasAlias('twig.extension.pagerfanta', 'pagerfanta.twig_extension'); $this->assertContainerBuilderHasAlias('white_october_pagerfanta.view_factory', 'pagerfanta.view_factory'); $this->assertContainerBuilderHasService('pagerfanta.view_factory', 'My\ViewFactory'); } protected function registerCompilerPass(ContainerBuilder $container): void { $container->addCompilerPass(new PagerfantaBridgePass()); } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/RegisterFormBuilderPassTest.php ================================================ setDefinition('sylius.registry.form_builder', new Definition()); $this->setDefinition( 'default_form_builder', (new Definition()) ->addTag('sylius.default_resource_form.builder', ['type' => 'foo']) ->addTag('sylius.default_resource_form.builder', ['type' => 'bar']), ); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.registry.form_builder', 'register', ['foo', new Reference('default_form_builder')], ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.registry.form_builder', 'register', ['bar', new Reference('default_form_builder')], ); } protected function registerCompilerPass(ContainerBuilder $container): void { $container->addCompilerPass(new RegisterFormBuilderPass()); } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/RegisterFqcnControllersPassTest.php ================================================ setDefinition('sylius.resource_registry', new Definition()); $this->setDefinition('app.controller.book', new Definition()); $this->setParameter( 'sylius.resources', [ 'app.book' => [ 'driver' => 'doctrine/orm', 'classes' => ['model' => BookTestClass::class, 'controller' => BookTestController::class], ], ], ); $this->compile(); $this->assertContainerBuilderHasService(BookTestController::class); } /** @test */ public function it_skips_the_alias_if_controller_class_is_not_defined(): void { $this->setDefinition('sylius.resource_registry', new Definition()); $this->setDefinition('app.controller.book', new Definition()); $this->setParameter( 'sylius.resources', [ 'app.book' => [ 'driver' => 'doctrine/orm', 'classes' => ['model' => BookTestClass::class], ], ], ); $this->compile(); $this->assertContainerBuilderNotHasService(BookTestController::class); } /** @test */ public function it_works_with_legacy_resource_interface(): void { $this->setDefinition('sylius.resource_registry', new Definition()); $this->setDefinition('app.controller.book', new Definition()); $this->setParameter( 'sylius.resources', [ 'app.book' => [ 'driver' => 'doctrine/orm', 'classes' => ['model' => LegacyBookTestClass::class, 'controller' => BookTestController::class], ], ], ); $this->compile(); $this->assertContainerBuilderHasService(BookTestController::class); } /** @test */ public function it_throws_exception_if_class_does_not_implement_resource_interface(): void { $this->expectException(InvalidArgumentException::class); $this->setParameter( 'sylius.resources', [ 'app.normalClass' => [ 'driver' => 'doctrine/orm', 'classes' => ['model' => NormalClass::class, 'controller' => BookTestController::class], ], ], ); $this->compile(); } protected function registerCompilerPass(ContainerBuilder $container): void { $container->addCompilerPass(new RegisterFqcnControllersPass()); } } class BookTestClass implements ResourceInterface { public function getId() { } } class NormalClass { } class BookTestController extends ResourceController { } class LegacyBookTestClass implements LegacyResourceInterface { public function getId() { } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/RegisterResourceRepositoryPassTest.php ================================================ setDefinition('sylius.registry.resource_repository', new Definition()); $this->setDefinition('sylius.repository.product', new Definition()); $this->setParameter( 'sylius.resources', ['sylius.product' => []], ); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.registry.resource_repository', 'register', ['sylius.product', new Reference('sylius.repository.product')], ); } /** * @test * * @doesNotPerformAssertions */ public function it_does_not_add_resource_repository_to_resource_repository_registry_if_registry_does_not_exist(): void { $this->setDefinition('sylius.repository.product', new Definition()); $this->setParameter( 'sylius.resources', ['sylius.product' => []], ); $this->compile(); } /** * @test */ public function it_does_not_add_resource_repository_to_resource_repository_registry_if_resources_do_not_exist(): void { $this->setDefinition('sylius.registry.resource_repository', new Definition()); $this->setDefinition('sylius.repository.product', new Definition()); $this->compile(); $this->assertContainerBuilderNotHasServiceDefinitionWithMethodCall( 'sylius.registry.resource_repository', 'register', ['sylius.product', new Reference('sylius.repository.product')], ); } /** * @test */ public function it_does_not_add_resource_repository_to_resource_repository_registry_if_resource_repository_does_not_exist(): void { $this->setDefinition('sylius.registry.resource_repository', new Definition()); $this->setParameter( 'sylius.resources', ['sylius.product' => []], ); $this->compile(); $this->assertContainerBuilderNotHasServiceDefinitionWithMethodCall( 'sylius.registry.resource_repository', 'register', ['sylius.product', new Reference('sylius.repository.product')], ); } protected function registerCompilerPass(ContainerBuilder $container): void { $container->addCompilerPass(new RegisterResourceRepositoryPass()); } private function assertContainerBuilderNotHasServiceDefinitionWithMethodCall( string $serviceId, string $method, array $arguments, ): void { $definition = $this->container->findDefinition($serviceId); self::assertThat( $definition, new LogicalNot(new DefinitionHasMethodCallConstraint($method, $arguments)), ); } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/RegisterResourceStateMachinePassTest.php ================================================ registerService('sylius.resource_controller.state_machine', StateMachine::class); $this->makeSymfonyWorkflowAvailable(); $this->makeWinzouStateMachineAvailable(); $this->setDefinition('sylius.resource_registry', new Definition()); $this->setParameter( 'sylius.resources', [ 'app.book' => ['state_machine_component' => 'symfony', 'classes' => ['model' => BookClass::class]], 'app.author' => ['state_machine_component' => 'winzou', 'classes' => ['model' => AuthorClass::class]], 'app.pull_request' => ['classes' => ['model' => PullRequestClass::class]], ], ); $this->compile(); $this->assertContainerBuilderHasAlias('app.controller_state_machine.book', 'sylius.resource_controller.state_machine.symfony'); $this->assertContainerBuilderHasAlias('app.controller_state_machine.author', 'sylius.resource_controller.state_machine.winzou'); $this->assertContainerBuilderHasAlias('app.controller_state_machine.pull_request', 'sylius.resource_controller.state_machine'); } /** @test */ public function it_throws_an_exception_when_specific_symfony_state_machine_component_is_not_available(): void { $this->registerService('sylius.resource_controller.state_machine', StateMachine::class); $this->makeWinzouStateMachineAvailable(); $this->setDefinition('sylius.resource_registry', new Definition()); $this->setParameter( 'sylius.resources', [ 'app.book' => ['state_machine_component' => 'symfony', 'classes' => ['model' => BookClass::class]], 'app.author' => ['state_machine_component' => 'winzou', 'classes' => ['model' => AuthorClass::class]], 'app.pull_request' => ['classes' => ['model' => PullRequestClass::class]], ], ); $error = false; try { $this->compile(); } catch (\LogicException $exception) { $error = true; $message = $exception->getMessage(); } $this->assertTrue($error, 'Should not compile'); $this->assertEquals('State machine "symfony" is not available.', $message); } /** @test */ public function it_throws_an_exception_when_specific_winzou_state_machine_component_is_not_available(): void { $this->registerService('sylius.resource_controller.state_machine', Workflow::class); $this->makeSymfonyWorkflowAvailable(); $this->setDefinition('sylius.resource_registry', new Definition()); $this->setParameter( 'sylius.resources', [ 'app.book' => ['state_machine_component' => 'symfony', 'classes' => ['model' => BookClass::class]], 'app.author' => ['state_machine_component' => 'winzou', 'classes' => ['model' => AuthorClass::class]], 'app.pull_request' => ['classes' => ['model' => PullRequestClass::class]], ], ); $error = false; try { $this->compile(); } catch (\LogicException $exception) { $error = true; $message = $exception->getMessage(); } $this->assertTrue($error, 'Should not compile'); $this->assertEquals('State machine "winzou" is not available.', $message); } protected function registerCompilerPass(ContainerBuilder $container): void { $container->addCompilerPass(new RegisterResourceStateMachinePass()); } private function makeWinzouStateMachineAvailable(): void { $this->registerService('sylius.resource_controller.state_machine.winzou', StateMachine::class); } private function makeSymfonyWorkflowAvailable(): void { $this->registerService('sylius.resource_controller.state_machine.symfony', Workflow::class); } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/RegisterResourcesPassTest.php ================================================ setDefinition('sylius.resource_registry', new Definition()); $this->setParameter( 'sylius.resources', [ 'app.book' => ['classes' => ['model' => BookClass::class]], 'app.author' => ['classes' => ['model' => AuthorClass::class]], 'app.legacy_book' => ['classes' => ['model' => LegacyBookClass::class]], ], ); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.resource_registry', 'addFromAliasAndConfiguration', ['app.book', ['classes' => ['model' => BookClass::class]]], ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.resource_registry', 'addFromAliasAndConfiguration', ['app.author', ['classes' => ['model' => AuthorClass::class]]], ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'sylius.resource_registry', 'addFromAliasAndConfiguration', ['app.legacy_book', ['classes' => ['model' => LegacyBookClass::class]]], ); } protected function registerCompilerPass(ContainerBuilder $container): void { $container->addCompilerPass(new RegisterResourcesPass()); } } class AbstractResource implements ResourceInterface { public function getId() { return; } } class BookClass extends AbstractResource { } class AuthorClass extends AbstractResource { } class PullRequestClass extends AbstractResource { } class LegacyBookClass implements LegacyResourceInterface { public function getId() { return; } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/RegisterStateMachinePassTest.php ================================================ setParameter('sylius.resource.settings', ['state_machine_component' => null]); $this->compile(); $this->assertContainerBuilderHasParameter('sylius.state_machine_component.default', null); $this->assertContainerBuilderNotHasService('sylius.resource_controller.state_machine'); $this->assertContainerBuilderNotHasService('sylius.resource_controller.state_machine.symfony'); $this->assertContainerBuilderNotHasService('sylius.resource_controller.state_machine.winzou'); } /** @test */ public function it_registers_state_machine_controller_with_symfony_workflow_when_its_configured_to(): void { $this->setParameter('sylius.resource.settings', ['state_machine_component' => ResourceBundleInterface::STATE_MACHINE_SYMFONY]); $this->makeSymfonyWorkflowAvailable(); $this->compile(); $this->assertContainerBuilderHasParameter('sylius.state_machine_component.default', 'symfony'); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine', Workflow::class); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine.symfony', Workflow::class); $this->assertContainerBuilderNotHasService('sylius.resource_controller.state_machine.winzou'); } /** @test */ public function it_registers_state_machine_controller_with_winzou_when_its_configured_to(): void { $this->setParameter('sylius.resource.settings', ['state_machine_component' => ResourceBundleInterface::STATE_MACHINE_WINZOU]); $this->makeWinzouStateMachineAvailable(); $this->compile(); $this->assertContainerBuilderHasParameter('sylius.state_machine_component.default', 'winzou'); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine', StateMachine::class); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine.winzou', StateMachine::class); $this->assertContainerBuilderNotHasService('sylius.resource_controller.state_machine.symfony'); } /** @test */ public function it_registers_state_machine_controller_with_symfony_workflow_by_default_when_only_this_one_is_available(): void { $this->setParameter('sylius.resource.settings', ['state_machine_component' => null]); $this->makeSymfonyWorkflowAvailable(); $this->compile(); $this->assertContainerBuilderHasParameter('sylius.state_machine_component.default', 'symfony'); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine', Workflow::class); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine.symfony', Workflow::class); $this->assertContainerBuilderNotHasService('sylius.resource_controller.state_machine.winzou'); } /** @test */ public function it_registers_state_machine_controller_with_winzou_by_default_when_only_this_one_is_available(): void { $this->setParameter('sylius.resource.settings', ['state_machine_component' => null]); $this->makeWinzouStateMachineAvailable(); $this->compile(); $this->assertContainerBuilderHasParameter('sylius.state_machine_component.default', 'winzou'); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine', StateMachine::class); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine.winzou', StateMachine::class); $this->assertContainerBuilderNotHasService('sylius.resource_controller.state_machine.symfony'); } /** @test */ public function it_registers_state_machine_controller_with_winzou_by_default_when_both_are_available(): void { $this->setParameter('sylius.resource.settings', ['state_machine_component' => null]); $this->makeWinzouStateMachineAvailable(); $this->makeSymfonyWorkflowAvailable(); $this->compile(); $this->assertContainerBuilderHasParameter('sylius.state_machine_component.default', 'winzou'); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine', StateMachine::class); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine.winzou', StateMachine::class); $this->assertContainerBuilderHasService('sylius.resource_controller.state_machine.symfony', Workflow::class); } protected function registerCompilerPass(ContainerBuilder $container): void { $this->setParameter('kernel.bundles', []); $this->setParameter('sylius.resource.settings', ['state_machine_component' => null]); $container->addCompilerPass(new RegisterStateMachinePass()); } private function makeWinzouStateMachineAvailable(): void { $this->setParameter('kernel.bundles', ['Winzou/StateMachine' => winzouStateMachineBundle::class]); $this->registerService('sm.factory', Factory::class); } private function makeSymfonyWorkflowAvailable(): void { $this->registerService('workflow.registry', SymfonyWorkflow::class); } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/UnregisterFosRestDefinitionsPassTest.php ================================================ setParameter('kernel.bundles', []); $this->compile(); $this->assertContainerBuilderNotHasService('sylius.resource_controller.view_handler'); } /** @test */ public function it_keeps_the_view_handler_if_fos_rest_is_available(): void { $this->markAsSkippedIfFosRestBundleIsNotAvailable(); $this->setParameter('kernel.bundles', [FOSRestBundle::class]); $this->compile(); $this->assertContainerBuilderHasService('sylius.resource_controller.view_handler'); } protected function registerCompilerPass(ContainerBuilder $container): void { $this->registerService('sylius.resource_controller.view_handler', ViewHandler::class); $this->setParameter('kernel.bundles', []); $container->addCompilerPass(new UnregisterFosRestDefinitionsPass()); } private function markAsSkippedIfFosRestBundleIsNotAvailable(): void { if (!class_exists(FOSRestBundle::class)) { $this->markTestSkipped('FriendsOfSymfony Rest Bundle is not installed.'); } } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/UnregisterHateoasDefinitionsPassTest.php ================================================ setParameter('kernel.bundles', []); $this->compile(); $this->assertContainerBuilderNotHasService('sylius.resource_controller.pagerfanta_representation_factory'); } /** @test */ public function it_keeps_the_view_handler_if_fos_rest_is_available(): void { $this->markAsSkippedIfHateoasIsNotAvailable(); $this->setParameter('kernel.bundles', [BazingaHateoasBundle::class]); $this->compile(); $this->assertContainerBuilderHasService('sylius.resource_controller.pagerfanta_representation_factory'); } protected function registerCompilerPass(ContainerBuilder $container): void { $this->registerService('sylius.resource_controller.pagerfanta_representation_factory', PagerfantaFactory::class); $this->setParameter('kernel.bundles', []); $container->addCompilerPass(new UnregisterHateoasDefinitionsPass()); } private function markAsSkippedIfHateoasIsNotAvailable(): void { if (!class_exists(BazingaHateoasBundle::class)) { $this->markTestSkipped('HateoasBundle is not installed.'); } } } ================================================ FILE: tests/Bundle/DependencyInjection/Compiler/WinzouStateMachinePassTest.php ================================================ getContainer(); $services = [ 'sm.factory', 'sm.callback_factory', 'sm.callback.cascade_transition', FactoryInterface::class, CallbackFactoryInterface::class, CascadeTransitionCallback::class, ]; foreach ($services as $id) { Assert::assertNotNull( $container->get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE), sprintf('Service "%s" could not be found', $id), ); } } } ================================================ FILE: tests/Bundle/DependencyInjection/Driver/Exception/InvalidDriverExceptionTest.php ================================================ invalidDriverException = new InvalidDriverException('driver', 'className'); } public function testHasAMessage(): void { $this->assertSame( 'Driver "driver" is not supported by className.', $this->invalidDriverException->getMessage(), ); } } ================================================ FILE: tests/Bundle/DependencyInjection/Driver/Exception/UnknownDriverExceptionTest.php ================================================ unknownDriverException = new UnknownDriverException('driver'); } public function testHasAMessage(): void { $this->assertSame('Unknown driver "driver".', $this->unknownDriverException->getMessage()); } } ================================================ FILE: tests/Bundle/DependencyInjection/Dummy/BookWithAliasResource.php ================================================ container->setParameter( 'kernel.bundles', [ 'BabDevPagerfantaBundle' => BabDevPagerfantaBundle::class, 'TwigBundle' => TwigBundle::class, ], ); $bundleConfig = [ 'default_view' => 'twitter_bootstrap', 'exceptions_strategy' => [ 'out_of_range_page' => 'custom', 'not_valid_current_page' => 'to_http_not_found', ], ]; // Prepend config now to allow the prepend pass to work $this->container->prependExtensionConfig('white_october_pagerfanta', $bundleConfig); $this->load($bundleConfig); $this->assertSame([$bundleConfig], $this->container->getExtensionConfig('babdev_pagerfanta')); $this->assertContainerBuilderHasParameter('white_october_pagerfanta.default_view', $bundleConfig['default_view']); } protected function getContainerExtensions(): array { return [ new PagerfantaExtension(), new BabDevPagerfantaExtension(), ]; } } ================================================ FILE: tests/Bundle/DependencyInjection/SyliusResourceExtensionTest.php ================================================ load([ 'resources' => [ 'app.book' => [ 'classes' => [ 'model' => Book::class, 'form' => BookType::class, ], 'translation' => [ 'classes' => [ 'model' => BookTranslation::class, ], ], ], ], ]); $this->assertContainerBuilderHasService('app.factory.book'); $this->assertContainerBuilderHasService('app.repository.book'); $this->assertContainerBuilderHasService('app.controller.book'); $this->assertContainerBuilderHasService('app.manager.book'); $this->assertContainerBuilderNotHasService(ResourceController::class); $this->assertContainerBuilderHasParameter('app.model.book.class', Book::class); $this->assertContainerBuilderHasParameter('app.model.book_translation.class', BookTranslation::class); $this->assertContainerBuilderHasParameter('app.form.book.class', BookType::class); } /** @test */ public function it_aliases_authorization_checker_with_the_one_given_in_configuration(): void { $this->load([ 'authorization_checker' => 'custom_service', ]); $this->assertContainerBuilderHasAlias('sylius.resource_controller.authorization_checker', 'custom_service'); } /** @test */ public function it_registers_default_translation_parameters(): void { $this->load([ 'translation' => [ 'locale_provider' => 'test.custom_locale_provider', ], ]); $this->assertContainerBuilderHasAlias('sylius.translation_locale_provider', 'test.custom_locale_provider'); } /** @test */ public function it_does_not_break_when_aliasing_two_resources_use_same_factory_class(): void { $this->load([ 'resources' => [ 'app.book' => [ 'classes' => [ 'model' => Book::class, 'factory' => BookFactory::class, ], ], 'app.comic_book' => [ 'classes' => [ 'model' => ComicBook::class, 'factory' => BookFactory::class, ], ], ], ]); $this->assertContainerBuilderHasService('app.factory.book'); $this->assertContainerBuilderHasService('app.factory.comic_book'); $this->assertContainerBuilderHasAlias(sprintf('%s $bookFactory', BookFactory::class), 'app.factory.book'); $this->assertContainerBuilderHasAlias(sprintf('%s $comicBookFactory', BookFactory::class), 'app.factory.comic_book'); } /** @test */ public function it_registers_parameter_for_paths(): void { $this->load([ 'mapping' => [ 'paths' => [ __DIR__ . '/Dummy', ], ], ]); $this->assertContainerBuilderHasParameter('sylius.resource.mapping', [ 'imports' => [], 'paths' => [ __DIR__ . '/Dummy', ], ]); } /** @test */ public function it_auto_registers_resources(): void { $this->load([ 'mapping' => [ 'paths' => [ __DIR__ . '/Dummy', ], ], ]); $this->assertContainerBuilderHasParameter('sylius.resources', [ 'app.book' => [ 'classes' => [ 'model' => BookWithAliasResource::class, 'controller' => ResourceController::class, 'factory' => Factory::class, 'form' => DefaultResourceType::class, ], 'driver' => 'doctrine/orm', ], 'my_app.book_with_application_name' => [ 'classes' => [ 'model' => BookWithApplicationNameResource::class, 'controller' => ResourceController::class, 'factory' => Factory::class, 'form' => DefaultResourceType::class, ], 'driver' => 'doctrine/orm', ], 'app.dummy' => [ 'classes' => [ 'model' => DummyResource::class, 'controller' => ResourceController::class, 'factory' => Factory::class, 'form' => DefaultResourceType::class, ], 'driver' => 'doctrine/orm', ], 'app.no_driver' => [ 'classes' => [ 'model' => NoDriverResource::class, 'controller' => ResourceController::class, 'factory' => Factory::class, 'form' => DefaultResourceType::class, ], 'driver' => false, ], ]); } /** @test */ public function it_registers_doctrine_related_services_when_doctrine_is_available(): void { $this->load(); $this->assertContainerBuilderHasService(ResourceMappingDriverChain::class); $this->assertContainerBuilderHasService(PersistProcessor::class); $this->assertContainerBuilderHasService(RemoveProcessor::class); } public function testItRegistersMetadataConfigurationWithADirectoryAsImportPath(): void { $this->load([ 'mapping' => [ 'imports' => [ __DIR__ . '/php', ], ], ]); $emptyPhpFile = realpath(__DIR__ . '/php/empty_file.php'); $this->assertContainerBuilderHasService('sylius.metadata.resource_extractor.php_file'); $this->assertSame([$emptyPhpFile], $this->container->getDefinition('sylius.metadata.resource_extractor.php_file')->getArgument(0)); } public function testItRegistersMetadataConfigurationWithAFileAsImportPath(): void { $this->load([ 'mapping' => [ 'imports' => [ __DIR__ . '/php/empty_file.php', ], ], ]); $emptyPhpFile = realpath(__DIR__ . '/php/empty_file.php'); $this->assertContainerBuilderHasService('sylius.metadata.resource_extractor.php_file'); $this->assertSame([$emptyPhpFile], $this->container->getDefinition('sylius.metadata.resource_extractor.php_file')->getArgument(0)); } public function testItRegistersRoutingPathBcLayerAutomatically(): void { $this->load(); if (class_exists(Transliterator::class)) { $this->assertTrue($this->container->getParameter('sylius.routing_path_bc_layer')); return; } $this->assertFalse($this->container->getParameter('sylius.routing_path_bc_layer')); } public function testRoutingPathBcLayerCanBeEnabled(): void { if (!class_exists(Transliterator::class)) { $this->markTestSkipped('This test requires Transliterator.'); } $this->load([ 'routing_path_bc_layer' => true, ]); $this->assertTrue($this->container->getParameter('sylius.routing_path_bc_layer')); } public function testRoutingPathBcLayerCanBeDisabled(): void { $this->load([ 'routing_path_bc_layer' => false, ]); $this->assertFalse($this->container->getParameter('sylius.routing_path_bc_layer')); } public function testItRegistersDashPathSegmentNameGeneratorByDefault(): void { $this->load(); $this->assertSame('sylius.metadata.path_segment_name_generator.dash', $this->container->getAlias('sylius.path_segment_name_generator')->__toString()); } public function testItRegistersCustomPathSegmentNameGenerator(): void { $this->load([ 'path_segment_name_generator' => 'sylius.metadata.path_segment_name_generator.underscore', ]); $this->assertSame('sylius.metadata.path_segment_name_generator.underscore', $this->container->getAlias('sylius.path_segment_name_generator')->__toString()); } protected function getContainerExtensions(): array { $this->setParameter('kernel.bundles', []); return [ new SyliusResourceExtension(), ]; } } ================================================ FILE: tests/Bundle/DependencyInjection/php/empty_file.php ================================================ markTestSkipped('Doctrine PHPCR ODM not installed'); } $this->documentManagerMock = $this->createMock(DocumentManagerInterface::class); $this->defaultParentListener = new DefaultParentListener($this->documentManagerMock, '/path/to'); $this->eventMock = $this->createMock(ResourceControllerEvent::class); $this->documentMetadataMock = $this->createMock(ClassMetadata::class); } public function testThrowAnExceptionIfNoParentMappingExists(): void { $this->eventMock->expects($this->once())->method('getSubject')->willReturn(new \stdClass()); $this->documentManagerMock ->expects($this->once()) ->method('getClassMetadata') ->with(\stdClass::class) ->willReturn($this->documentMetadataMock) ; $this->documentMetadataMock->parentMapping = null; $this->expectException(\RuntimeException::class); $this->expectExceptionMessage( 'A default parent path has been specified, but no parent mapping has been applied to document "stdClass"', ); $this->defaultParentListener->onPreCreate($this->eventMock); } public function testThrowAnExceptionIfTheParentDoesNotExistAndAutocreateIsFalse(): void { $this->defaultParentListener = new DefaultParentListener($this->documentManagerMock, '/path/to', false); $this->eventMock->expects($this->once())->method('getSubject')->willReturn(new \stdClass()); $this->documentManagerMock ->expects($this->once()) ->method('getClassMetadata') ->with(\stdClass::class) ->willReturn($this->documentMetadataMock) ; $this->documentMetadataMock->parentMapping = 'parent'; $this->documentManagerMock ->expects($this->once()) ->method('find') ->with(null, '/path/to') ->willReturn(null) ; $this->expectException(\RuntimeException::class); $this->expectExceptionMessage( 'Document at default parent path "/path/to" does not exist. `autocreate` was set to "false"', ); $this->defaultParentListener->onPreCreate($this->eventMock); } public function testSetTheParentDocument(): void { $subjectDocument = new \stdClass(); $parentDocument = new \stdClass(); $this->eventMock->expects($this->once())->method('getSubject')->willReturn($subjectDocument); $this->documentManagerMock ->expects($this->once()) ->method('getClassMetadata') ->with(\stdClass::class) ->willReturn($this->documentMetadataMock) ; $this->documentMetadataMock->parentMapping = 'parent'; $this->documentMetadataMock ->expects($this->once()) ->method('getFieldValue') ->with($subjectDocument, 'parent') ->willReturn(null) ; $this->documentManagerMock ->expects($this->once()) ->method('find') ->with(null, '/path/to') ->willReturn($parentDocument) ; $this->documentMetadataMock ->expects($this->once()) ->method('setFieldValue') ->with($subjectDocument, 'parent', $parentDocument) ; $this->defaultParentListener->onPreCreate($this->eventMock); } public function testAutocreateAndSetTheParentDocument(): void { /** @var SessionInterface|MockObject $sessionMock */ $sessionMock = $this->createMock(SessionInterface::class); /** @var NodeInterface|MockObject $nodeMock */ $nodeMock = $this->createMock(NodeInterface::class); $this->defaultParentListener = new DefaultParentListener($this->documentManagerMock, '/path/to', true); $subjectDocument = new \stdClass(); $parentDocument = new \stdClass(); $this->eventMock->expects($this->once())->method('getSubject')->willReturn($subjectDocument); $this->documentManagerMock ->expects($this->once()) ->method('getClassMetadata') ->with(\stdClass::class) ->willReturn($this->documentMetadataMock) ; $this->documentMetadataMock->parentMapping = 'parent'; $this->documentManagerMock ->expects($this->exactly(2)) ->method('find') ->with(null, '/path/to') ->willReturn(null, $parentDocument) ; $this->documentManagerMock ->expects($this->once()) ->method('getPhpcrSession') ->willReturn($sessionMock) ; $sessionMock->expects($this->once())->method('getRootNode')->willReturn($nodeMock); // we need to mock the behavior of the node helper // see: https://github.com/phpcr/phpcr-utils/issues/106 $nodeMock ->expects($this->exactly(2)) ->method('hasNode') ->with($this->anything()) ->willReturn(true) ; $nodeMock ->expects($this->exactly(2)) ->method('getNode') ->with($this->anything()) ->willReturn($nodeMock) ; $this->documentMetadataMock ->expects($this->once()) ->method('setFieldValue') ->with($subjectDocument, 'parent', $parentDocument) ; $this->defaultParentListener->onPreCreate($this->eventMock); } public function testSetTheParentDocumentIfForceIsTrueAndTheParentIsAlreadySet(): void { $this->defaultParentListener = new DefaultParentListener($this->documentManagerMock, '/path/to', false, true); $subjectDocument = new \stdClass(); $parentDocument = new \stdClass(); $this->eventMock->expects($this->once())->method('getSubject')->willReturn($subjectDocument); $this->documentManagerMock ->expects($this->once()) ->method('getClassMetadata') ->with(\stdClass::class) ->willReturn($this->documentMetadataMock) ; $this->documentMetadataMock ->expects($this->never()) ->method('getFieldValue') ->with($subjectDocument, 'parent') ; $this->documentMetadataMock ->expects($this->once()) ->method('setFieldValue') ->with($subjectDocument, 'parent', $parentDocument) ; $this->documentMetadataMock->parentMapping = 'parent'; $this->documentManagerMock ->expects($this->once()) ->method('find') ->with(null, '/path/to') ->willReturn($parentDocument) ; $this->defaultParentListener->onPreCreate($this->eventMock); } public function testReturnEarlyIfForceIsFalseAndSubjectAlreadyHasAParent(): void { $subjectDocument = new \stdClass(); $this->eventMock->expects($this->once())->method('getSubject')->willReturn($subjectDocument); $this->documentManagerMock ->expects($this->once()) ->method('getClassMetadata') ->with(\stdClass::class) ->willReturn($this->documentMetadataMock) ; $this->documentMetadataMock->parentMapping = 'parent'; $this->documentMetadataMock ->expects($this->once()) ->method('getFieldValue') ->with($subjectDocument, 'parent') ->willReturn(new \stdClass()) ; $this->documentManagerMock->expects($this->never())->method('find')->with(null, '/path/to'); $this->defaultParentListener->onPreCreate($this->eventMock); } } ================================================ FILE: tests/Bundle/Doctrine/ODM/PHPCR/EventListener/NameFilterListenerTest.php ================================================ markTestSkipped('Doctrine PHPCR ODM not installed'); } $this->documentManagerMock = $this->createMock(DocumentManagerInterface::class); $this->nameFilterListener = new NameFilterListener($this->documentManagerMock); $this->eventMock = $this->createMock(ResourceControllerEvent::class); $this->metadataMock = $this->createMock(ClassMetadata::class); $this->document = new \stdClass(); } public function testThrowsAnExceptionIfNodenameIsNotMapped(): void { $this->eventMock->expects($this->once())->method('getSubject')->willReturn($this->document); $this->documentManagerMock->expects($this->once())->method('getClassMetadata')->with('stdClass')->willReturn($this->metadataMock); $this->metadataMock->nodename = null; $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('In order to use the node name filter on "stdClass" it is necessary to map a field as the "nodename"'); $this->nameFilterListener->onEvent($this->eventMock); } public function testCleanTheName(): void { $this->eventMock->expects($this->once())->method('getSubject')->willReturn($this->document); $this->documentManagerMock->expects($this->once())->method('getClassMetadata')->with('stdClass')->willReturn($this->metadataMock); $this->metadataMock->nodename = 'foobar'; $this->metadataMock->expects($this->once())->method('getFieldValue')->with($this->document, 'foobar')->willReturn('Hello//Foo'); $this->metadataMock->expects($this->once())->method('setFieldValue')->with($this->document, 'foobar', 'Hello Foo'); $this->nameFilterListener->onEvent($this->eventMock); } public function testUseTheGivenReplacementChar(): void { $this->nameFilterListener = new NameFilterListener($this->documentManagerMock, '_'); $this->eventMock->expects($this->once())->method('getSubject')->willReturn($this->document); $this->documentManagerMock->expects($this->once())->method('getClassMetadata')->with('stdClass')->willReturn($this->metadataMock); $this->metadataMock->nodename = 'foobar'; $this->metadataMock->expects($this->once())->method('getFieldValue')->with($this->document, 'foobar')->willReturn('Hello//Foo'); $this->metadataMock->expects($this->once())->method('setFieldValue')->with($this->document, 'foobar', 'Hello__Foo'); $this->nameFilterListener->onEvent($this->eventMock); } } ================================================ FILE: tests/Bundle/Doctrine/ODM/PHPCR/EventListener/NameResolverListenerTest.php ================================================ markTestSkipped('Doctrine PHPCR ODM not installed'); } $this->documentManagerMock = $this->createMock(DocumentManagerInterface::class); $this->nameResolverListener = new NameResolverListener($this->documentManagerMock); $this->eventMock = $this->createMock(ResourceControllerEvent::class); $this->metadataMock = $this->createMock(ClassMetadata::class); $this->nodeMock = $this->createMock(NodeInterface::class); $this->document = new \stdClass(); $this->parentDocument = new \stdClass(); } public function testThrowsAnExceptionWhenTheGeneratorTypeIsNotParent(): void { $this->eventMock->expects($this->once())->method('getSubject')->willReturn($this->document); $this->documentManagerMock ->expects($this->once()) ->method('getClassMetadata') ->with('stdClass') ->willReturn($this->metadataMock) ; $this->metadataMock->idGenerator = 1; $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Document of class "stdClass" must be using the GENERATOR_TYPE_PARENT identificatio strategy (value 3), it is current using "1" (this may be an automatic configuration: be sure to map both the `nodename` and the `parentDocument`).'); $this->nameResolverListener->onEvent($this->eventMock); } public function testRetainTheOriginalNameWhenNoConflictExists(): void { $this->eventMock->expects($this->once())->method('getSubject')->willReturn($this->document); $this->documentManagerMock->expects($this->once())->method('getClassMetadata')->with('stdClass')->willReturn($this->metadataMock); $this->documentManagerMock ->expects($this->once()) ->method('getNodeForDocument') ->with($this->parentDocument) ->willReturn($this->nodeMock) ; $this->metadataMock->idGenerator = ClassMetadata::GENERATOR_TYPE_PARENT; $this->metadataMock->nodename = 'title'; $this->metadataMock->parentMapping = 'parent'; $this->metadataMock ->expects($this->exactly(2)) ->method('getFieldValue') ->willReturnMap([ [$this->document, 'parent', $this->parentDocument], [$this->document, 'title', 'Hello World'], ]) ; $this->nodeMock->expects($this->once())->method('getPath')->willReturn('/path/to'); $this->documentManagerMock->expects($this->once())->method('find')->with(null, '/path/to/Hello World')->willReturn(null); $this->metadataMock->expects($this->once())->method('setFieldValue')->with($this->document, 'title', 'Hello World'); $this->nameResolverListener->onEvent($this->eventMock); } public function testAutoIncrementTheNameIfAConflictExists(): void { $existingDocument = new \stdClass(); $this->eventMock->expects($this->once())->method('getSubject')->willReturn($this->document); $this->documentManagerMock ->expects($this->once()) ->method('getClassMetadata') ->with('stdClass') ->willReturn($this->metadataMock) ; $this->metadataMock->idGenerator = ClassMetadata::GENERATOR_TYPE_PARENT; $this->metadataMock->nodename = 'title'; $this->metadataMock->parentMapping = 'parent'; $this->metadataMock ->expects($this->exactly(2)) ->method('getFieldValue') ->willReturnMap([ [$this->document, 'parent', $this->parentDocument], [$this->document, 'title', 'Hello World'], ]) ; $this->documentManagerMock->expects($this->once())->method('getNodeForDocument')->with($this->parentDocument)->willReturn($this->nodeMock); $this->nodeMock->expects($this->once())->method('getPath')->willReturn('/path/to'); $this->documentManagerMock ->expects($this->exactly(4)) ->method('find') ->willReturnMap([ [null, '/path/to/Hello World', $existingDocument], [null, '/path/to/Hello World-1', $existingDocument], [null, '/path/to/Hello World-2', $existingDocument], [null, '/path/to/Hello World-3', null], ]) ; $this->metadataMock->expects($this->once())->method('setFieldValue')->with($this->document, 'title', 'Hello World-3'); $this->nameResolverListener->onEvent($this->eventMock); } } ================================================ FILE: tests/Bundle/Doctrine/ORM/Form/Builder/DefaultFormBuilderTest.php ================================================ entityManagerMock = $this->createMock(EntityManagerInterface::class); $this->defaultFormBuilder = new DefaultFormBuilder($this->entityManagerMock); $this->metadataMock = $this->createMock(MetadataInterface::class); $this->formBuilderMock = $this->createMock(FormBuilderInterface::class); $this->classMetadataMock = $this->createMock(ClassMetadata::class); } public function testADefaultFormBuilder(): void { $this->assertInstanceOf(DefaultFormBuilderInterface::class, $this->defaultFormBuilder); } public function testDoesNotSupportEntitiesWithMultiplePrimaryKeys(): void { $this->metadataMock->expects($this->once())->method('getClass')->with('model')->willReturn('AppBundle\Entity\Book'); $this->entityManagerMock->expects($this->once())->method('getClassMetadata')->with('AppBundle\Entity\Book')->willReturn($this->classMetadataMock); $this->classMetadataMock->identifier = ['id', 'slug']; $this->expectException(\RuntimeException::class); $this->defaultFormBuilder->build($this->metadataMock, $this->formBuilderMock, []); } public function testExcludesNonNaturalIdentifierFromTheFieldList(): void { $this->metadataMock->expects($this->once())->method('getClass')->with('model')->willReturn('AppBundle\Entity\Book'); $this->entityManagerMock->expects($this->once())->method('getClassMetadata')->with('AppBundle\Entity\Book')->willReturn($this->classMetadataMock); $this->classMetadataMock->fieldNames = ['id', 'name', 'description', 'enabled']; $this->classMetadataMock->identifier = ['id']; $this->classMetadataMock->expects($this->once())->method('isIdentifierNatural')->willReturn(false); $this->classMetadataMock->expects($this->once())->method('getAssociationMappings')->willReturn([]); $this->classMetadataMock ->expects($this->exactly(3)) ->method('getTypeOfField') ->willReturnMap([ ['name', Types::STRING], ['description', Types::TEXT], ['enabled', Types::BOOLEAN], ]) ; $this->formBuilderMock ->expects($this->exactly(3)) ->method('add') ->willReturnMap([ ['name', null, [], $this->formBuilderMock], ['description', null, [], $this->formBuilderMock], ['enabled', null, [], $this->formBuilderMock], ]) ; $this->defaultFormBuilder->build($this->metadataMock, $this->formBuilderMock, []); } public function testDoesNotExcludeNaturalIdentifierFromTheFieldList(): void { $this->metadataMock->expects($this->once())->method('getClass')->with('model')->willReturn('AppBundle\Entity\Book'); $this->entityManagerMock->expects($this->once())->method('getClassMetadata')->with('AppBundle\Entity\Book')->willReturn($this->classMetadataMock); $this->classMetadataMock->fieldNames = ['id', 'name', 'description', 'enabled']; $this->classMetadataMock->identifier = ['id']; $this->classMetadataMock->expects($this->once())->method('isIdentifierNatural')->willReturn(true); $this->classMetadataMock->expects($this->once())->method('getAssociationMappings')->willReturn([]); $this->classMetadataMock ->expects($this->exactly(4)) ->method('getTypeOfField') ->willReturnMap([ ['id', Types::INTEGER], ['name', Types::STRING], ['description', Types::TEXT], ['enabled', Types::BOOLEAN], ]) ; $this->formBuilderMock ->expects($this->exactly(4)) ->method('add') ->willReturnMap([ ['id', null, [], $this->formBuilderMock], ['name', null, [], $this->formBuilderMock], ['description', null, [], $this->formBuilderMock], ['enabled', null, [], $this->formBuilderMock], ]) ; $this->defaultFormBuilder->build($this->metadataMock, $this->formBuilderMock, []); } public function testUsesMetadataToCreateAppropriateFields(): void { $this->metadataMock->expects($this->once())->method('getClass')->with('model')->willReturn('AppBundle\Entity\Book'); $this->entityManagerMock->expects($this->once())->method('getClassMetadata')->with('AppBundle\Entity\Book')->willReturn($this->classMetadataMock); $this->classMetadataMock->fieldNames = ['name', 'description', 'enabled']; $this->classMetadataMock->expects($this->once())->method('isIdentifierNatural')->willReturn(true); $this->classMetadataMock->expects($this->once())->method('getAssociationMappings')->willReturn([]); $this->classMetadataMock ->expects($this->exactly(3)) ->method('getTypeOfField') ->willReturnMap([ ['name', Types::STRING], ['description', Types::TEXT], ['enabled', Types::BOOLEAN], ]) ; $this->formBuilderMock ->expects($this->exactly(3)) ->method('add') ->willReturnMap([ ['name', null, [], $this->formBuilderMock], ['description', null, [], $this->formBuilderMock], ['enabled', null, [], $this->formBuilderMock], ]) ; $this->defaultFormBuilder->build($this->metadataMock, $this->formBuilderMock, []); } public function testUsesSingleTextWidgetForDatetimeField(): void { $this->metadataMock->expects($this->once())->method('getClass')->with('model')->willReturn('AppBundle\Entity\Book'); $this->entityManagerMock->expects($this->once())->method('getClassMetadata')->with('AppBundle\Entity\Book')->willReturn($this->classMetadataMock); $this->classMetadataMock->fieldNames = ['name', 'description', 'enabled', 'publishedAt']; $this->classMetadataMock->expects($this->once())->method('isIdentifierNatural')->willReturn(true); $this->classMetadataMock->expects($this->once())->method('getAssociationMappings')->willReturn([]); $this->classMetadataMock ->expects($this->exactly(4)) ->method('getTypeOfField') ->willReturnMap([ ['name', Types::STRING], ['description', Types::TEXT], ['enabled', Types::BOOLEAN], ['publishedAt', Types::DATETIME_MUTABLE], ]) ; $this->formBuilderMock ->expects($this->exactly(4)) ->method('add') ->willReturnMap([ ['name', null, [], $this->formBuilderMock], ['description', null, [], $this->formBuilderMock], ['enabled', null, [], $this->formBuilderMock], ['publishedAt', null, ['widget' => 'single_text'], $this->formBuilderMock], ]) ; $this->defaultFormBuilder->build($this->metadataMock, $this->formBuilderMock, []); } public function testAlsoCreatesFieldsForRelationsOtherThanOneToMany(): void { $this->metadataMock->expects($this->once())->method('getClass')->with('model')->willReturn('AppBundle\Entity\Book'); $this->entityManagerMock->expects($this->once())->method('getClassMetadata')->with('AppBundle\Entity\Book')->willReturn($this->classMetadataMock); $this->classMetadataMock->fieldNames = ['name', 'description', 'enabled', 'publishedAt']; $this->classMetadataMock->expects($this->once())->method('isIdentifierNatural')->willReturn(true); $this->classMetadataMock->expects($this->once())->method('getAssociationMappings')->willReturn([ 'category' => ['type' => ClassMetadata::MANY_TO_ONE], 'users' => ['type' => ClassMetadata::ONE_TO_MANY], ]); $this->classMetadataMock ->expects($this->exactly(4)) ->method('getTypeOfField') ->willReturnMap([ ['name', Types::STRING], ['description', Types::TEXT], ['enabled', Types::BOOLEAN], ['publishedAt', Types::DATETIME_MUTABLE], ]) ; $this->formBuilderMock ->expects($this->exactly(5)) ->method('add') ->willReturnMap([ ['name', null, [], $this->formBuilderMock], ['description', null, [], $this->formBuilderMock], ['enabled', null, [], $this->formBuilderMock], ['publishedAt', null, ['widget' => 'single_text'], $this->formBuilderMock], ['category', null, ['choice_label' => 'id'], $this->formBuilderMock], ]) ; $this->defaultFormBuilder->build($this->metadataMock, $this->formBuilderMock, []); } public function testExcludesCommonFieldsLikeCreatedAtAndUpdatedAt(): void { $this->metadataMock->expects($this->once())->method('getClass')->with('model')->willReturn('AppBundle\Entity\Book'); $this->entityManagerMock->expects($this->once())->method('getClassMetadata')->with('AppBundle\Entity\Book')->willReturn($this->classMetadataMock); $this->classMetadataMock->fieldNames = ['name', 'description', 'enabled', 'createdAt', 'updatedAt']; $this->classMetadataMock->expects($this->once())->method('isIdentifierNatural')->willReturn(true); $this->classMetadataMock->expects($this->once())->method('getAssociationMappings')->willReturn([]); $this->classMetadataMock ->expects($this->exactly(3)) ->method('getTypeOfField') ->willReturnMap([ ['name', Types::STRING], ['description', Types::TEXT], ['enabled', Types::BOOLEAN], ['createdAt', Types::DATETIME_MUTABLE], ['updatedAt', Types::DATETIME_MUTABLE], ]) ; $this->formBuilderMock ->expects($this->exactly(3)) ->method('add') ->willReturnMap([ ['name', null, [], $this->formBuilderMock], ['description', null, [], $this->formBuilderMock], ['enabled', null, [], $this->formBuilderMock], ]) ; $this->defaultFormBuilder->build($this->metadataMock, $this->formBuilderMock, []); } } ================================================ FILE: tests/Bundle/Event/ResourceControllerEventTest.php ================================================ event = new ResourceControllerEvent(); $this->event->setMessage('message'); } public function testItHasEmptyMessageByDefault(): void { $event = new ResourceControllerEvent(); $this->assertSame('', $event->getMessage()); } public function testItCanSetAndGetMessage(): void { $this->event->setMessage('custom_message'); $this->assertSame('custom_message', $this->event->getMessage()); } public function testItHasEmptyMessageTypeByDefault(): void { $this->assertSame('', $this->event->getMessageType()); } public function testItCanSetAndGetMessageType(): void { $this->event->setMessageType(GenericEvent::TYPE_SUCCESS); $this->assertSame(GenericEvent::TYPE_SUCCESS, $this->event->getMessageType()); } public function testItHasEmptyMessageParametersByDefault(): void { $this->assertSame([], $this->event->getMessageParameters()); } public function testItCanSetAndGetMessageParameters(): void { $this->event->setMessageParameters(['parameter_1', 'parameter_2']); $this->assertSame(['parameter_1', 'parameter_2'], $this->event->getMessageParameters()); } public function testItIsNotStoppedByDefault(): void { $this->assertFalse($this->event->isStopped()); } public function testItCanBeStopped(): void { $this->event->stop('error_message'); $this->assertTrue($this->event->isStopped()); } public function testItStopsPropagationWhenStopped(): void { $this->event->stop('error_message', GenericEvent::TYPE_SUCCESS, ['parameter']); $this->assertTrue($this->event->isPropagationStopped()); $this->assertSame('error_message', $this->event->getMessage()); $this->assertSame(GenericEvent::TYPE_SUCCESS, $this->event->getMessageType()); $this->assertSame(['parameter'], $this->event->getMessageParameters()); } public function testItDoesNotHaveResponseByDefault(): void { $this->assertFalse($this->event->hasResponse()); } public function testItCanSetAndGetResponse(): void { $response = new Response(); $this->event->setResponse($response); $this->assertSame($response, $this->event->getResponse()); $this->assertTrue($this->event->hasResponse()); } } ================================================ FILE: tests/Bundle/EventListener/AbstractDoctrineListenerTest.php ================================================ entityManager = $this->getContainer()->get(EntityManagerInterface::class); $resourceRegistry = $this->getContainer()->get(RegistryInterface::class); $this->listener = new class($resourceRegistry) extends AbstractDoctrineListener { public function publicIsResource(ClassMetadata $metadata): bool { return $this->isResource($metadata); } public function publicGetReflectionService(): \Doctrine\Persistence\Mapping\ReflectionService { return $this->getReflectionService(); } }; } public function testIsResourceReturnsTrueForResourceInterface(): void { $metadata = $this->getClassMetadata(Book::class); $result = $this->listener->publicIsResource($metadata); $this->assertTrue($result); } public function testGetReflectionServiceReturnsRuntimeReflectionService(): void { $reflectionService = $this->listener->publicGetReflectionService(); $this->assertInstanceOf(RuntimeReflectionService::class, $reflectionService); } public function testGetReflectionServiceReturnsSameInstanceOnMultipleCalls(): void { $firstCall = $this->listener->publicGetReflectionService(); $secondCall = $this->listener->publicGetReflectionService(); $this->assertSame($firstCall, $secondCall); } public function testConstructorAcceptsResourceRegistry(): void { $resourceRegistry = $this->getContainer()->get(RegistryInterface::class); $listener = new class($resourceRegistry) extends AbstractDoctrineListener { }; $this->assertInstanceOf(AbstractDoctrineListener::class, $listener); } public function testIsResourceWorksWithDifferentEntities(): void { $bookMetadata = $this->getClassMetadata(Book::class); $userMetadata = $this->getClassMetadata(User::class); $this->assertTrue($this->listener->publicIsResource($bookMetadata)); $this->assertTrue($this->listener->publicIsResource($userMetadata)); } public function testReflectionServiceImplementsCorrectInterface(): void { $reflectionService = $this->listener->publicGetReflectionService(); $this->assertInstanceOf(ReflectionService::class, $reflectionService); } public function testReflectionServiceIsLazilyInitialized(): void { $resourceRegistry = $this->getContainer()->get(RegistryInterface::class); $newListener = new class($resourceRegistry) extends AbstractDoctrineListener { public function hasReflectionService(): bool { return $this->getReflectionService() instanceof RuntimeReflectionService; } }; $this->assertTrue($newListener->hasReflectionService()); } private function getClassMetadata(string $className): ClassMetadata { return $this->entityManager->getClassMetadata($className); } } ================================================ FILE: tests/Bundle/EventListener/ODMRepositoryClassSubscriberTest.php ================================================ markTestSkipped('Doctrine MongoDB ODM is not installed.'); } $this->registry = $this->createMock(RegistryInterface::class); $this->subscriber = new ODMRepositoryClassSubscriber($this->registry); } public function testItImplementsEventSubscriberInterface(): void { $this->assertInstanceOf(EventSubscriber::class, $this->subscriber); } public function testItSubscribesToLoadClassMetadataEvent(): void { $this->assertSame([Events::loadClassMetadata], $this->subscriber->getSubscribedEvents()); } public function testItSetsCustomRepositoryClassWhenConfigured(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata ->expects($this->once()) ->method('hasClass') ->with('repository') ->willReturn(true); $metadata ->expects($this->once()) ->method('getClass') ->with('repository') ->willReturn('FooRepository'); $this->registry ->expects($this->once()) ->method('getByClass') ->with('Foo') ->willReturn($metadata); $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata ->expects($this->once()) ->method('getName') ->willReturn('Foo'); $classMetadata ->expects($this->once()) ->method('setCustomRepositoryClass') ->with('FooRepository'); $documentManager = $this->createMock(DocumentManager::class); $event = new LoadClassMetadataEventArgs($classMetadata, $documentManager); $this->subscriber->loadClassMetadata($event); } public function testItDoesNotSetCustomRepositoryClassWhenNotConfigured(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata ->expects($this->once()) ->method('hasClass') ->with('repository') ->willReturn(false); $this->registry ->expects($this->once()) ->method('getByClass') ->with('Foo') ->willReturn($metadata); $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata ->expects($this->once()) ->method('getName') ->willReturn('Foo'); $classMetadata ->expects($this->never()) ->method('setCustomRepositoryClass'); $documentManager = $this->createMock(DocumentManager::class); $event = new LoadClassMetadataEventArgs($classMetadata, $documentManager); $this->subscriber->loadClassMetadata($event); } public function testItDoesNotSetCustomRepositoryClassWhenClassNotInRegistry(): void { $this->registry ->expects($this->once()) ->method('getByClass') ->with('Foo') ->willThrowException(new \InvalidArgumentException()); $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata ->expects($this->once()) ->method('getName') ->willReturn('Foo'); $classMetadata ->expects($this->never()) ->method('setCustomRepositoryClass'); $documentManager = $this->createMock(DocumentManager::class); $event = new LoadClassMetadataEventArgs($classMetadata, $documentManager); $this->subscriber->loadClassMetadata($event); } } ================================================ FILE: tests/Bundle/EventListener/ORMMappedSuperClassSubscriberTest.php ================================================ wakeupReflection(new RuntimeReflectionService()); $configuration = new Configuration(); $configuration->setNamingStrategy($namingStrategy); $configuration->setMetadataDriverImpl(new class() implements MappingDriver { /** @param ClassMetadata $metadata */ public function loadMetadataForClass(string $className, ClassMetadataInterface $metadata): void { if ($className !== ParentEntity::class) { return; } $metadata->isMappedSuperclass = true; $metadata->mapOneToOne([ 'fieldName' => 'relatedEntity', 'targetEntity' => ParentEntity::class, 'joinColumns' => [['name' => 'related_entity_id', 'referencedColumnName' => 'id']], ]); } /** @return list */ public function getAllClassNames(): array { return [ParentEntity::class]; } public function isTransient(string $className): bool { return false; } }); $em = $this->createMock(EntityManagerInterface::class); $em->method('getConfiguration')->willReturn($configuration); $event = new LoadClassMetadataEventArgs($childMetadata, $em); $subscriber->loadClassMetadata($event); self::assertArrayHasKey('relatedEntity', $childMetadata->associationMappings); $association = $childMetadata->associationMappings['relatedEntity']; $sourceEntity = \is_array($association) ? $association['sourceEntity'] : $association->sourceEntity; self::assertSame(ChildEntity::class, $sourceEntity); } } ================================================ FILE: tests/Bundle/EventListener/ORMRepositoryClassSubscriberTest.php ================================================ registry = $this->createMock(RegistryInterface::class); $this->subscriber = new ORMRepositoryClassSubscriber($this->registry); } public function testItImplementsEventSubscriberInterface(): void { $this->assertInstanceOf(EventSubscriber::class, $this->subscriber); } public function testItSubscribesToLoadClassMetadataEvent(): void { $this->assertSame([Events::loadClassMetadata], $this->subscriber->getSubscribedEvents()); } public function testItSetsCustomRepositoryClassWhenConfigured(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata ->expects($this->once()) ->method('hasClass') ->with('repository') ->willReturn(true); $metadata ->expects($this->once()) ->method('getClass') ->with('repository') ->willReturn('FooRepository'); $this->registry ->expects($this->once()) ->method('getByClass') ->with('Foo') ->willReturn($metadata); $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata ->expects($this->once()) ->method('getName') ->willReturn('Foo'); $classMetadata ->expects($this->once()) ->method('setCustomRepositoryClass') ->with('FooRepository'); $event = $this->createMock(LoadClassMetadataEventArgs::class); $event ->expects($this->once()) ->method('getClassMetadata') ->willReturn($classMetadata); $this->subscriber->loadClassMetadata($event); } public function testItDoesNotSetCustomRepositoryClassWhenNotConfigured(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata ->expects($this->once()) ->method('hasClass') ->with('repository') ->willReturn(false); $this->registry ->expects($this->once()) ->method('getByClass') ->with('Foo') ->willReturn($metadata); $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata ->expects($this->once()) ->method('getName') ->willReturn('Foo'); $classMetadata ->expects($this->never()) ->method('setCustomRepositoryClass'); $event = $this->createMock(LoadClassMetadataEventArgs::class); $event ->expects($this->once()) ->method('getClassMetadata') ->willReturn($classMetadata); $this->subscriber->loadClassMetadata($event); } public function testItDoesNotSetCustomRepositoryClassWhenClassNotInRegistry(): void { $this->registry ->expects($this->once()) ->method('getByClass') ->with('Foo') ->willThrowException(new \InvalidArgumentException()); $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata ->expects($this->once()) ->method('getName') ->willReturn('Foo'); $classMetadata ->expects($this->never()) ->method('setCustomRepositoryClass'); $event = $this->createMock(LoadClassMetadataEventArgs::class); $event ->expects($this->once()) ->method('getClassMetadata') ->willReturn($classMetadata); $this->subscriber->loadClassMetadata($event); } } ================================================ FILE: tests/Bundle/EventListener/ORMTranslatableListenerTest.php ================================================ assertInstanceOf( EntityRepository::class, $this->getEntityManager()->getRepository(BookTranslationInterface::class), ); } private function getEntityManager(): EntityManagerInterface { return $this->getContainer()->get(EntityManagerInterface::class); } } ================================================ FILE: tests/Bundle/ExpressionLanguage/ExpressionLanguageTest.php ================================================ assertInstanceOf(ExpressionLanguage::class, $expressionLanguage); } public function testItCanBeInstantiatedWithCacheItemPool(): void { $cache = $this->createMock(CacheItemPoolInterface::class); $expressionLanguage = new ExpressionLanguage($cache); $this->assertInstanceOf(ExpressionLanguage::class, $expressionLanguage); } public function testItCanBeInstantiatedWithProviders(): void { $provider = $this->createMock(ExpressionFunctionProviderInterface::class); $provider->method('getFunctions')->willReturn([]); $expressionLanguage = new ExpressionLanguage(null, [$provider]); $this->assertInstanceOf(ExpressionLanguage::class, $expressionLanguage); } public function testItThrowsExceptionForInvalidCacheType(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Cache argument has to implement Psr\Cache\CacheItemPoolInterface'); new ExpressionLanguage('invalid'); } public function testItRegistersNotNullExpressionFunctionProvider(): void { $expressionLanguage = new ExpressionLanguage(); $result = $expressionLanguage->evaluate('notFoundOnNull(value)', ['value' => 'test']); $this->assertSame('test', $result); } public function testItCompilesExpressionsWithNotFoundOnNullFunction(): void { $expressionLanguage = new ExpressionLanguage(); $compiled = $expressionLanguage->compile('notFoundOnNull(value)', ['value']); $this->assertStringContainsString('null !== ', $compiled); } } ================================================ FILE: tests/Bundle/ExpressionLanguage/NotNullExpressionFunctionProviderTest.php ================================================ provider = new NotNullExpressionFunctionProvider(); } public function testItProvidesExpressionFunctions(): void { $functions = $this->provider->getFunctions(); $this->assertIsArray($functions); $this->assertCount(1, $functions); $this->assertInstanceOf(ExpressionFunction::class, $functions[0]); } public function testItProvidesNotFoundOnNullFunction(): void { $functions = $this->provider->getFunctions(); $this->assertSame('notFoundOnNull', $functions[0]->getName()); } public function testItReturnsValueWhenNotNull(): void { $functions = $this->provider->getFunctions(); $evaluator = $functions[0]->getEvaluator(); $result = $evaluator([], 'some value'); $this->assertSame('some value', $result); } public function testItThrowsNotFoundExceptionWhenNull(): void { $functions = $this->provider->getFunctions(); $evaluator = $functions[0]->getEvaluator(); $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage('Requested page is invalid.'); $evaluator([], null); } public function testItCompilesExpressionCorrectly(): void { $functions = $this->provider->getFunctions(); $compiler = $functions[0]->getCompiler(); $compiled = $compiler('$result'); $this->assertStringContainsString('null !== $result', $compiled); $this->assertStringContainsString('$result', $compiled); $this->assertStringContainsString('throw new NotFoundHttpException', $compiled); } } ================================================ FILE: tests/Bundle/Fixtures/AnimalInterface.php ================================================ id; } } ================================================ FILE: tests/Bundle/Fixtures/Resource.php ================================================ transformer = new CollectionToStringTransformer(','); } public function testImplementsDataTransformerInterface(): void { self::assertInstanceOf(DataTransformerInterface::class, $this->transformer); } public function testTransformsCollectionToString(): void { $collection = new ArrayCollection(['abc', 'def', 'ghi', 'jkl']); $result = $this->transformer->transform($collection); self::assertSame('abc,def,ghi,jkl', $result); } public function testTransformsEmptyCollectionToEmptyString(): void { $result = $this->transformer->transform(new ArrayCollection()); self::assertSame('', $result); } public function testTransformsStringToCollection(): void { $result = $this->transformer->reverseTransform('abc,def,ghi,jkl'); self::assertEquals(new ArrayCollection(['abc', 'def', 'ghi', 'jkl']), $result); } public function testTransformsEmptyStringToEmptyCollection(): void { $result = $this->transformer->reverseTransform(''); self::assertEquals(new ArrayCollection(), $result); } public function testTransformsCollectionWithSingleElement(): void { $result = $this->transformer->transform(new ArrayCollection(['single'])); self::assertSame('single', $result); } public function testReverseTransformsSingleValue(): void { $result = $this->transformer->reverseTransform('single'); self::assertEquals(new ArrayCollection(['single']), $result); } public function testThrowsExceptionWhenTransformingNonCollection(): void { self::expectException(TransformationFailedException::class); self::expectExceptionMessage('Expected "Doctrine\Common\Collections\Collection", but got "stdClass"'); $this->transformer->transform(new \stdClass()); } public function testThrowsExceptionWhenReverseTransformingNonString(): void { self::expectException(TransformationFailedException::class); self::expectExceptionMessage('Expected string, but got "stdClass"'); $this->transformer->reverseTransform(new \stdClass()); } } ================================================ FILE: tests/Bundle/Form/DataTransformer/RecursiveTransformerTest.php ================================================ &MockObject */ private DataTransformerInterface $decoratedTransformer; private RecursiveTransformer $transformer; protected function setUp(): void { $this->decoratedTransformer = $this->createMock(DataTransformerInterface::class); $this->transformer = new RecursiveTransformer($this->decoratedTransformer); } public function testImplementsDataTransformerInterface(): void { self::assertInstanceOf(DataTransformerInterface::class, $this->transformer); } public function testReturnsEmptyCollectionWhenTransformingNull(): void { $this->decoratedTransformer ->expects(self::never()) ->method('transform'); $result = $this->transformer->transform(null); self::assertEquals(new ArrayCollection(), $result); } public function testReturnsEmptyCollectionWhenReverseTransformingNull(): void { $this->decoratedTransformer ->expects(self::never()) ->method('reverseTransform'); $result = $this->transformer->reverseTransform(null); self::assertEquals(new ArrayCollection(), $result); } public function testTransformsCollectionRecursively(): void { $this->decoratedTransformer ->expects(self::exactly(3)) ->method('transform') ->willReturnCallback(fn (string $value): string => strtolower($value)); $result = $this->transformer->transform(new ArrayCollection(['ABC', 'CDE', 'FGH'])); self::assertEquals(new ArrayCollection(['abc', 'cde', 'fgh']), $result); } public function testReverseTransformsCollectionRecursively(): void { $this->decoratedTransformer ->expects(self::exactly(3)) ->method('reverseTransform') ->willReturnCallback(fn (string $value): string => strtoupper($value)); $result = $this->transformer->reverseTransform(new ArrayCollection(['abc', 'cde', 'fgh'])); self::assertEquals(new ArrayCollection(['ABC', 'CDE', 'FGH']), $result); } public function testThrowsExceptionWhenTransformingNonCollection(): void { self::expectException(TransformationFailedException::class); self::expectExceptionMessage('Expected "Doctrine\Common\Collections\Collection", but got "stdClass"'); /** @phpstan-ignore argument.type */ $this->transformer->transform(new \stdClass()); } public function testThrowsExceptionWhenReverseTransformingNonCollection(): void { self::expectException(TransformationFailedException::class); self::expectExceptionMessage('Expected "Doctrine\Common\Collections\Collection", but got "stdClass"'); /** @phpstan-ignore argument.type */ $this->transformer->reverseTransform(new \stdClass()); } } ================================================ FILE: tests/Bundle/Form/DataTransformer/ResourceToIdentifierTransformerTest.php ================================================ &MockObject */ private RepositoryInterface $repository; private ResourceToIdentifierTransformer $transformer; protected function setUp(): void { $this->repository = $this->createMock(RepositoryInterface::class); $this->transformer = new ResourceToIdentifierTransformer($this->repository, 'id'); } public function testImplementsDataTransformerInterface(): void { self::assertInstanceOf(DataTransformerInterface::class, $this->transformer); } public function testTransformsNullValueToNull(): void { $result = $this->transformer->transform(null); self::assertNull($result); } public function testTransformsResourceToIdentifier(): void { $resource = $this->createMock(ResourceInterface::class); $resource ->expects(self::once()) ->method('getId') ->willReturn(6); $this->repository ->expects(self::once()) ->method('getClassName') ->willReturn(ResourceInterface::class); $result = $this->transformer->transform($resource); self::assertSame(6, $result); } public function testReverseTransformsNullToNull(): void { $result = $this->transformer->reverseTransform(null); self::assertNull($result); } public function testReverseTransformsIdentifierToResource(): void { $resource = $this->createMock(ResourceInterface::class); $this->repository ->expects(self::once()) ->method('findOneBy') ->with(['id' => 5]) ->willReturn($resource); $result = $this->transformer->reverseTransform(5); self::assertSame($resource, $result); } public function testThrowsExceptionWhenResourceDoesNotExist(): void { $this->repository ->expects(self::once()) ->method('findOneBy') ->with(['id' => 6]) ->willReturn(null); $this->repository ->expects(self::once()) ->method('getClassName') ->willReturn(ResourceInterface::class); self::expectException(TransformationFailedException::class); self::expectExceptionMessage('Object "Sylius\Resource\Model\ResourceInterface" with identifier "id"="6" does not exist.'); $this->transformer->reverseTransform(6); } } ================================================ FILE: tests/Bundle/Form/EventSubscriber/AddCodeFormSubscriberTest.php ================================================ 'preSetData'], $events); } private function createFormEvent(mixed $data, FormInterface $form): FormEvent { $event = $this->createMock(FormEvent::class); $event->method('getData')->willReturn($data); $event->method('getForm')->willReturn($form); return $event; } public function testAddsEnabledCodeFieldWhenResourceIsNew(): void { $subscriber = new AddCodeFormSubscriber(); $resource = $this->createMock(CodeAwareInterface::class); $resource->method('getCode')->willReturn(null); $form = $this->createMock(FormInterface::class); $form ->expects(self::once()) ->method('add') ->with( 'code', TextType::class, self::callback(fn (array $options): bool => $options['disabled'] === false), ) ->willReturn($form); $subscriber->preSetData($this->createFormEvent($resource, $form)); } public function testAddsDisabledCodeFieldWhenResourceHasCode(): void { $subscriber = new AddCodeFormSubscriber(); $resource = $this->createMock(CodeAwareInterface::class); $resource->method('getCode')->willReturn('existing_code'); $form = $this->createMock(FormInterface::class); $form ->expects(self::once()) ->method('add') ->with( 'code', TextType::class, self::callback(fn (array $options): bool => $options['disabled'] === true), ) ->willReturn($form); $subscriber->preSetData($this->createFormEvent($resource, $form)); } public function testAddsEnabledCodeFieldWhenResourceIsNull(): void { $subscriber = new AddCodeFormSubscriber(); $form = $this->createMock(FormInterface::class); $form ->expects(self::once()) ->method('add') ->with( 'code', TextType::class, self::callback(fn (array $options): bool => $options['disabled'] === false), ) ->willReturn($form); $subscriber->preSetData($this->createFormEvent(null, $form)); } public function testThrowsExceptionWhenResourceDoesNotImplementCodeAwareInterface(): void { $subscriber = new AddCodeFormSubscriber(); $event = $this->createMock(FormEvent::class); $event->method('getData')->willReturn(new \stdClass()); self::expectException(UnexpectedTypeException::class); $subscriber->preSetData($event); } public function testUsesCustomTypeAndOptions(): void { $subscriber = new AddCodeFormSubscriber(FormType::class, ['label' => 'custom.label']); $resource = $this->createMock(CodeAwareInterface::class); $resource->method('getCode')->willReturn('code'); $form = $this->createMock(FormInterface::class); $form ->expects(self::once()) ->method('add') ->with( 'code', FormType::class, self::callback(function (array $options): bool { return $options['label'] === 'custom.label' && $options['disabled'] === true; }), ) ->willReturn($form); $subscriber->preSetData($this->createFormEvent($resource, $form)); } } ================================================ FILE: tests/Bundle/Form/Extension/CollectionTypeExtensionTest.php ================================================ extension = new CollectionTypeExtension(); } public function testExtendsAbstractTypeExtension(): void { self::assertInstanceOf(AbstractTypeExtension::class, $this->extension); } public function testGetExtendedType(): void { self::assertSame(CollectionType::class, $this->extension->getExtendedType()); } public function testGetExtendedTypes(): void { self::assertSame([CollectionType::class], $this->extension->getExtendedTypes()); } public function testConfigureOptionsWithDefaults(): void { $resolver = new OptionsResolver(); $this->extension->configureOptions($resolver); $options = $resolver->resolve(); self::assertSame('sylius.form.collection.add', $options['button_add_label']); self::assertSame('sylius.form.collection.delete', $options['button_delete_label']); } public function testConfigureOptionsWithCustomLabels(): void { $resolver = new OptionsResolver(); $this->extension->configureOptions($resolver); $options = $resolver->resolve([ 'button_add_label' => 'custom.add', 'button_delete_label' => 'custom.delete', ]); self::assertSame('custom.add', $options['button_add_label']); self::assertSame('custom.delete', $options['button_delete_label']); } public function testBuildViewAddsButtonLabelsToViewVars(): void { $view = new FormView(); $form = $this->createMock(FormInterface::class); $options = [ 'button_add_label' => 'app.add_item', 'button_delete_label' => 'app.remove_item', ]; $this->extension->buildView($view, $form, $options); self::assertArrayHasKey('button_add_label', $view->vars); self::assertArrayHasKey('button_delete_label', $view->vars); self::assertSame('app.add_item', $view->vars['button_add_label']); self::assertSame('app.remove_item', $view->vars['button_delete_label']); } public function testBuildViewAddsDefaultLabelsToViewVars(): void { $view = new FormView(); $form = $this->createMock(FormInterface::class); $options = [ 'button_add_label' => 'sylius.form.collection.add', 'button_delete_label' => 'sylius.form.collection.delete', ]; $this->extension->buildView($view, $form, $options); self::assertSame('sylius.form.collection.add', $view->vars['button_add_label']); self::assertSame('sylius.form.collection.delete', $view->vars['button_delete_label']); } } ================================================ FILE: tests/Bundle/Form/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php ================================================ handler = new HttpFoundationRequestHandler(); } public function testImplementsRequestHandlerInterface(): void { self::assertInstanceOf(RequestHandlerInterface::class, $this->handler); } public function testThrowsExceptionWhenRequestIsNotHttpFoundationRequest(): void { $form = $this->createMock(FormInterface::class); self::expectException(UnexpectedTypeException::class); $this->handler->handleRequest($form, new \stdClass()); } public function testSubmitsGetRequestWithEmptyFormName(): void { $form = $this->createFormMock(''); $request = Request::create('/', 'GET', ['foo' => 'bar']); $form ->expects(self::once()) ->method('submit') ->with(['foo' => 'bar'], true); $this->handler->handleRequest($form, $request); } public function testDoesNotSubmitGetRequestWhenFormNameNotInQuery(): void { $form = $this->createFormMock('user'); $request = Request::create('/', 'GET', ['foo' => 'bar']); $form ->expects(self::never()) ->method('submit'); $this->handler->handleRequest($form, $request); } public function testSubmitsGetRequestWithFormNameInQuery(): void { $form = $this->createFormMock('user'); $request = Request::create('/', 'GET', ['user' => ['name' => 'John']]); $form ->expects(self::once()) ->method('submit') ->with(['name' => 'John'], true); $this->handler->handleRequest($form, $request); } public function testSubmitsPostRequestWithEmptyFormName(): void { $form = $this->createFormMock(''); $request = Request::create('/', 'POST', ['foo' => 'bar']); $form ->expects(self::once()) ->method('submit') ->with(['foo' => 'bar'], true); $this->handler->handleRequest($form, $request); } public function testDoesNotSubmitPostRequestWhenFormNameNotInRequest(): void { $form = $this->createFormMock('user'); $request = Request::create('/', 'POST', ['foo' => 'bar']); $form ->expects(self::never()) ->method('submit'); $this->handler->handleRequest($form, $request); } public function testSubmitsPostRequestWithFormNameInRequest(): void { $form = $this->createFormMock('user'); $request = Request::create('/', 'POST', ['user' => ['name' => 'John']]); $form ->expects(self::once()) ->method('submit') ->with(['name' => 'John'], true); $this->handler->handleRequest($form, $request); } public function testSubmitsPatchRequestWithoutClearingMissingFields(): void { $form = $this->createFormMock('user'); $request = Request::create('/', 'PATCH', ['user' => ['name' => 'John']]); $form ->expects(self::once()) ->method('submit') ->with(['name' => 'John'], false); $this->handler->handleRequest($form, $request); } public function testMergesFilesWithPostData(): void { $form = $this->createFormMock('user'); $file = new UploadedFile(__FILE__, 'test.php', null, null, true); $request = Request::create('/', 'POST', ['user' => ['name' => 'John']], [], ['user' => ['avatar' => $file]]); $form ->expects(self::once()) ->method('submit') ->with(['name' => 'John', 'avatar' => $file], true); $this->handler->handleRequest($form, $request); } public function testHandlesPostMaxSizeExceeded(): void { $serverParams = $this->createMock(ServerParams::class); $serverParams ->expects(self::once()) ->method('hasPostMaxSizeBeenExceeded') ->willReturn(true); $serverParams ->expects(self::once()) ->method('getNormalizedIniPostMaxSize') ->willReturn('8M'); $handler = new HttpFoundationRequestHandler($serverParams); $config = $this->createMock(FormConfigInterface::class); $config ->expects(self::once()) ->method('getOption') ->with('upload_max_size_message') ->willReturn(fn (): string => 'Upload size exceeded'); $form = $this->createMock(FormInterface::class); $form->method('getName')->willReturn('user'); $form->method('getConfig')->willReturn($config); $form ->expects(self::once()) ->method('submit') ->with(null, false); $form ->expects(self::once()) ->method('addError') ->with(self::callback(function ($error): bool { return $error->getMessage() === 'Upload size exceeded'; })); $request = Request::create('/', 'POST', ['user' => ['name' => 'John']]); $handler->handleRequest($form, $request); } public function testIsFileUploadReturnsTrueForFileInstance(): void { $file = $this->createMock(File::class); self::assertTrue($this->handler->isFileUpload($file)); } public function testIsFileUploadReturnsFalseForNonFileInstance(): void { self::assertFalse($this->handler->isFileUpload('string')); self::assertFalse($this->handler->isFileUpload(123)); self::assertFalse($this->handler->isFileUpload([])); self::assertFalse($this->handler->isFileUpload(null)); } public function testHandlesHeadRequest(): void { $form = $this->createFormMock('user'); $request = Request::create('/', 'HEAD', ['user' => ['name' => 'John']]); $form ->expects(self::once()) ->method('submit') ->with(['name' => 'John'], true); $this->handler->handleRequest($form, $request); } public function testHandlesTraceRequest(): void { $form = $this->createFormMock('user'); $request = Request::create('/', 'TRACE', ['user' => ['name' => 'John']]); $form ->expects(self::once()) ->method('submit') ->with(['name' => 'John'], true); $this->handler->handleRequest($form, $request); } /** @return FormInterface&MockObject */ private function createFormMock(string $name): FormInterface { $config = $this->createMock(FormConfigInterface::class); $config->method('getCompound')->willReturn(true); $form = $this->createMock(FormInterface::class); $form->method('getName')->willReturn($name); $form->method('getConfig')->willReturn($config); return $form; } } ================================================ FILE: tests/Bundle/Form/Registry/FormTypeRegistryTest.php ================================================ registry = new FormTypeRegistry(); } public function testImplementsFormTypeRegistryInterface(): void { self::assertInstanceOf(FormTypeRegistryInterface::class, $this->registry); } public function testAddsFormType(): void { $this->registry->add('app.product', 'default', 'App\Form\ProductType'); self::assertTrue($this->registry->has('app.product', 'default')); } public function testGetReturnsFormTypeWhenExists(): void { $this->registry->add('app.product', 'default', 'App\Form\ProductType'); self::assertSame('App\Form\ProductType', $this->registry->get('app.product', 'default')); } public function testGetReturnsNullWhenFormTypeDoesNotExist(): void { self::assertNull($this->registry->get('app.product', 'default')); } public function testHasReturnsFalseWhenFormTypeDoesNotExist(): void { self::assertFalse($this->registry->has('app.product', 'default')); } public function testHasReturnsTrueWhenFormTypeExists(): void { $this->registry->add('app.product', 'default', 'App\Form\ProductType'); self::assertTrue($this->registry->has('app.product', 'default')); } public function testSupportsMultipleIdentifiers(): void { $this->registry->add('app.product', 'default', 'App\Form\ProductType'); $this->registry->add('app.category', 'default', 'App\Form\CategoryType'); self::assertSame('App\Form\ProductType', $this->registry->get('app.product', 'default')); self::assertSame('App\Form\CategoryType', $this->registry->get('app.category', 'default')); } public function testSupportsMultipleTypeIdentifiersForSameIdentifier(): void { $this->registry->add('app.product', 'default', 'App\Form\ProductType'); $this->registry->add('app.product', 'admin', 'App\Form\AdminProductType'); self::assertSame('App\Form\ProductType', $this->registry->get('app.product', 'default')); self::assertSame('App\Form\AdminProductType', $this->registry->get('app.product', 'admin')); } public function testOverwritesFormTypeWhenAddingWithSameIdentifiers(): void { $this->registry->add('app.product', 'default', 'App\Form\ProductType'); $this->registry->add('app.product', 'default', 'App\Form\NewProductType'); self::assertSame('App\Form\NewProductType', $this->registry->get('app.product', 'default')); } public function testHasReturnsFalseForDifferentTypeIdentifier(): void { $this->registry->add('app.product', 'default', 'App\Form\ProductType'); self::assertFalse($this->registry->has('app.product', 'admin')); } public function testGetReturnsNullForDifferentTypeIdentifier(): void { $this->registry->add('app.product', 'default', 'App\Form\ProductType'); self::assertNull($this->registry->get('app.product', 'admin')); } } ================================================ FILE: tests/Bundle/Form/Type/AbstractResourceTypeTest.php ================================================ createFormType('App\Entity\Product'); self::assertInstanceOf(AbstractType::class, $formType); } public function testConstructorSetsDataClass(): void { $formType = $this->createFormType('App\Entity\Product'); $resolver = new OptionsResolver(); $formType->configureOptions($resolver); $options = $resolver->resolve(); self::assertSame('App\Entity\Product', $options['data_class']); } public function testConstructorSetsValidationGroups(): void { $formType = $this->createFormType('App\Entity\Product', ['Default', 'product_create']); $resolver = new OptionsResolver(); $formType->configureOptions($resolver); $options = $resolver->resolve(); self::assertSame(['Default', 'product_create'], $options['validation_groups']); } public function testConfigureOptionsWithDefaultValidationGroups(): void { $formType = $this->createFormType('App\Entity\Product'); $resolver = new OptionsResolver(); $formType->configureOptions($resolver); $options = $resolver->resolve(); self::assertArrayHasKey('validation_groups', $options); self::assertSame([], $options['validation_groups']); } public function testConfigureOptionsCanBeOverridden(): void { $formType = $this->createFormType('App\Entity\Product', ['Default']); $resolver = new OptionsResolver(); $formType->configureOptions($resolver); $options = $resolver->resolve([ 'data_class' => 'App\Entity\Category', 'validation_groups' => ['custom'], ]); self::assertSame('App\Entity\Category', $options['data_class']); self::assertSame(['custom'], $options['validation_groups']); } public function testConfigureOptionsWithMultipleValidationGroups(): void { $formType = $this->createFormType( 'App\Entity\Product', ['Default', 'product_create', 'strict_validation'], ); $resolver = new OptionsResolver(); $formType->configureOptions($resolver); $options = $resolver->resolve(); self::assertSame(['Default', 'product_create', 'strict_validation'], $options['validation_groups']); } public function testConfigureOptionsSetsDataClassAsDefault(): void { $formType = $this->createFormType('App\Entity\Order'); $resolver = new OptionsResolver(); $formType->configureOptions($resolver); $options = $resolver->resolve(); self::assertArrayHasKey('data_class', $options); self::assertSame('App\Entity\Order', $options['data_class']); } /** * @param string[] $validationGroups */ private function createFormType(string $dataClass, array $validationGroups = []): AbstractResourceType { return new class($dataClass, $validationGroups) extends AbstractResourceType { }; } } ================================================ FILE: tests/Bundle/Form/Type/ArchivableTypeTest.php ================================================ formType = new ArchivableType(); } public function testExtendsAbstractType(): void { self::assertInstanceOf(AbstractType::class, $this->formType); } public function testGetBlockPrefix(): void { self::assertSame('sylius_archivable', $this->formType->getBlockPrefix()); } public function testBuildFormAddsArchivedAtField(): void { $builder = $this->createMock(FormBuilderInterface::class); $builder ->expects(self::once()) ->method('add') ->with('archivedAt', DateTimeType::class) ->willReturn($builder); $builder ->method('addEventListener') ->willReturn($builder); $this->formType->buildForm($builder, []); } public function testBuildFormAddsSubmitEventListener(): void { $builder = $this->createMock(FormBuilderInterface::class); $builder ->method('add') ->willReturn($builder); $builder ->expects(self::once()) ->method('addEventListener') ->with( self::identicalTo('form.submit'), self::callback(fn ($callback): bool => is_callable($callback)), ) ->willReturn($builder); $this->formType->buildForm($builder, []); } public function testEventListenerSetsArchivedAtWhenNull(): void { $archivable = $this->createMock(ArchivableInterface::class); $archivable ->expects(self::once()) ->method('getArchivedAt') ->willReturn(null); $archivable ->expects(self::once()) ->method('setArchivedAt') ->with(self::isInstanceOf(\DateTime::class)); $listener = $this->captureEventListener(); $event = $this->createFormEvent($archivable); $listener($event); } public function testEventListenerKeepsArchivedAtWhenAlreadySet(): void { $existingDate = new \DateTime('2024-01-15 10:00:00'); $archivable = $this->createMock(ArchivableInterface::class); $archivable ->expects(self::once()) ->method('getArchivedAt') ->willReturn($existingDate); $archivable ->expects(self::once()) ->method('setArchivedAt') ->with(null); $listener = $this->captureEventListener(); $event = $this->createFormEvent($archivable); $listener($event); } public function testEventListenerSetsDataOnEvent(): void { $archivable = $this->createMock(ArchivableInterface::class); $archivable ->method('getArchivedAt') ->willReturn(null); $listener = $this->captureEventListener(); $event = $this->createFormEvent($archivable); $event ->expects(self::once()) ->method('setData') ->with($archivable); $listener($event); } private function captureEventListener(): callable { $listener = null; $builder = $this->createMock(FormBuilderInterface::class); $builder ->method('add') ->willReturn($builder); $builder ->expects(self::once()) ->method('addEventListener') ->willReturnCallback(function ($eventName, $callback) use (&$listener, $builder) { $listener = $callback; return $builder; }); $this->formType->buildForm($builder, []); return $listener; } /** @return FormEvent&MockObject */ private function createFormEvent(ArchivableInterface $archivable): FormEvent { $form = $this->createMock(FormInterface::class); $event = $this->createMock(FormEvent::class); $event ->method('getData') ->willReturn($archivable); $event ->method('getForm') ->willReturn($form); return $event; } } ================================================ FILE: tests/Bundle/Form/Type/DefaultResourceTypeTest.php ================================================ metadataRegistry = $this->createMock(RegistryInterface::class); $this->formBuilderRegistry = $this->createMock(ServiceRegistryInterface::class); $this->formType = new DefaultResourceType($this->metadataRegistry, $this->formBuilderRegistry); } public function testExtendsAbstractType(): void { self::assertInstanceOf(AbstractType::class, $this->formType); } public function testGetBlockPrefix(): void { self::assertSame('sylius_resource', $this->formType->getBlockPrefix()); } public function testBuildFormGetsMetadataByClass(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata ->method('getDriver') ->willReturn('doctrine/orm'); $this->metadataRegistry ->expects(self::once()) ->method('getByClass') ->with('App\Entity\Product') ->willReturn($metadata); $formBuilder = $this->createMock(DefaultFormBuilderInterface::class); $this->formBuilderRegistry ->method('get') ->willReturn($formBuilder); $builder = $this->createMock(FormBuilderInterface::class); $this->formType->buildForm($builder, ['data_class' => 'App\Entity\Product']); } public function testBuildFormGetsFormBuilderByDriver(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata ->method('getDriver') ->willReturn('doctrine/orm'); $this->metadataRegistry ->method('getByClass') ->willReturn($metadata); $formBuilder = $this->createMock(DefaultFormBuilderInterface::class); $this->formBuilderRegistry ->expects(self::once()) ->method('get') ->with('doctrine/orm') ->willReturn($formBuilder); $builder = $this->createMock(FormBuilderInterface::class); $this->formType->buildForm($builder, ['data_class' => 'App\Entity\Product']); } public function testBuildFormCallsFormBuilderBuild(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata ->method('getDriver') ->willReturn('doctrine/orm'); $this->metadataRegistry ->method('getByClass') ->willReturn($metadata); $formBuilder = $this->createMock(DefaultFormBuilderInterface::class); $builder = $this->createMock(FormBuilderInterface::class); $options = ['data_class' => 'App\Entity\Product', 'foo' => 'bar']; $formBuilder ->expects(self::once()) ->method('build') ->with($metadata, $builder, $options); $this->formBuilderRegistry ->method('get') ->willReturn($formBuilder); $this->formType->buildForm($builder, $options); } public function testBuildFormThrowsExceptionWhenNoDriver(): void { $metadata = $this->createMock(MetadataInterface::class); $metadata ->method('getDriver') ->willReturn(false); $metadata ->method('getAlias') ->willReturn('app.product'); $this->metadataRegistry ->method('getByClass') ->willReturn($metadata); $builder = $this->createMock(FormBuilderInterface::class); self::expectException(InvalidArgumentException::class); self::expectExceptionMessage('Form "Sylius\Bundle\ResourceBundle\Form\Type\DefaultResourceType" cannot be used with no driver configured on the resource "app.product". Please define a form.'); $this->formType->buildForm($builder, ['data_class' => 'App\Entity\Product']); } public function testBuildFormThrowsExceptionWhenDataClassIsNotString(): void { $builder = $this->createMock(FormBuilderInterface::class); self::expectException(InvalidArgumentException::class); $this->formType->buildForm($builder, ['data_class' => null]); } } ================================================ FILE: tests/Bundle/Form/Type/FixedCollectionTypeTest.php ================================================ factory->create(FixedCollectionType::class, null, [ 'entries' => ['first_name', 'last_name'], 'entry_type' => TextType::class, 'entry_name' => function (string $entry): string { return strtoupper($entry); }, ]); $form->submit(['FIRST_NAME' => 'Elon', 'LAST_NAME' => 'Tusk']); $this->assertEquals(['FIRST_NAME' => 'Elon', 'LAST_NAME' => 'Tusk'], $form->getData()); } /** * @test */ public function it_builds_fixed_collection_using_callable_to_resolve_entry_type(): void { $form = $this->factory->create(FixedCollectionType::class, null, [ 'entries' => ['first_name', 'last_name'], 'entry_type' => function (string $entry): string { if (!in_array($entry, ['first_name', 'last_name'], true)) { throw new \Exception(); } return TextType::class; }, 'entry_name' => function (string $entry): string { return strtoupper($entry); }, ]); $form->submit(['FIRST_NAME' => 'Elon', 'LAST_NAME' => 'Tusk']); $this->assertEquals(['FIRST_NAME' => 'Elon', 'LAST_NAME' => 'Tusk'], $form->getData()); } /** * @test */ public function it_builds_fixed_collection_using_array_to_resolve_entry_options(): void { $form = $this->factory->create(FixedCollectionType::class, null, [ 'entries' => ['first_name', 'last_name'], 'entry_type' => TextType::class, 'entry_name' => function (string $entry): string { return strtoupper($entry); }, 'entry_options' => [ 'empty_data' => 'Tusk', ], ]); $form->submit(['FIRST_NAME' => 'Elon']); $this->assertEquals(['FIRST_NAME' => 'Elon', 'LAST_NAME' => 'Tusk'], $form->getData()); } /** * @test */ public function it_builds_fixed_collection_using_callable_to_resolve_entry_options(): void { $form = $this->factory->create(FixedCollectionType::class, null, [ 'entries' => ['first_name', 'last_name'], 'entry_type' => TextType::class, 'entry_name' => function (string $entry): string { return strtoupper($entry); }, 'entry_options' => function (string $entry): array { $defaults = [ 'first_name' => 'Elon', 'last_name' => 'Tusk', ]; return ['empty_data' => $defaults[$entry]]; }, ]); $form->submit([]); $this->assertEquals(['FIRST_NAME' => 'Elon', 'LAST_NAME' => 'Tusk'], $form->getData()); } } ================================================ FILE: tests/Bundle/Form/Type/ResourceAutocompleteChoiceTypeTest.php ================================================ resourceRepositoryRegistry = $this->createMock(ServiceRegistryInterface::class); parent::setUp(); } protected function getExtensions(): array { $resourceAutoCompleteType = new ResourceAutocompleteChoiceType($this->resourceRepositoryRegistry); return [ new PreloadedExtension([$resourceAutoCompleteType], []), ]; } /** * @test */ public function it_returns_resource_from_its_code(): void { /** @var MockObject|RepositoryInterface $resourceRepository */ $resourceRepository = $this->createMock(RepositoryInterface::class); $resource = $this->createMock(ResourceInterface::class); $this->resourceRepositoryRegistry->method('get')->with('sylius.resource')->willReturn($resourceRepository); $resourceRepository->method('findOneBy')->with(['code' => 'mug'])->willReturn($resource); $form = $this->factory->create( ResourceAutocompleteChoiceType::class, null, ['resource' => 'sylius.resource', 'choice_name' => 'name', 'choice_value' => 'code'], ); $form->submit('mug'); $this->assertEquals($resource, $form->getData()); } /** * @test */ public function it_returns_resource_from_its_id(): void { /** @var MockObject|RepositoryInterface $resourceRepository */ $resourceRepository = $this->createMock(RepositoryInterface::class); $resource = $this->createMock(ResourceInterface::class); $this->resourceRepositoryRegistry->method('get')->with('sylius.resource')->willReturn($resourceRepository); $resourceRepository->method('findOneBy')->with(['id' => '1'])->willReturn($resource); $form = $this->factory->create( ResourceAutocompleteChoiceType::class, null, ['resource' => 'sylius.resource', 'choice_name' => 'name', 'choice_value' => 'id'], ); $form->submit('1'); $this->assertEquals($resource, $form->getData()); } /** * @test */ public function it_returns_different_resource_from_its_identifier(): void { /** @var MockObject|RepositoryInterface $resourceRepository */ $resourceRepository = $this->createMock(RepositoryInterface::class); $resource = $this->createMock(ResourceInterface::class); $this->resourceRepositoryRegistry->method('get')->with('sylius.zone')->willReturn($resourceRepository); $resourceRepository->method('findOneBy')->with(['code' => 'eu'])->willReturn($resource); $form = $this->factory->create( ResourceAutocompleteChoiceType::class, null, ['resource' => 'sylius.zone', 'choice_name' => 'name', 'choice_value' => 'code'], ); $form->submit('eu'); $this->assertEquals($resource, $form->getData()); } /** * @test */ public function it_has_identifier_as_view_value(): void { /** @var MockObject|RepositoryInterface $resourceRepository */ $resourceRepository = $this->createMock(RepositoryInterface::class); $resource = $this->createMock(ResourceInterface::class); $this->resourceRepositoryRegistry->method('get')->with('sylius.zone')->willReturn($resourceRepository); $resourceRepository->method('findOneBy')->with(['code' => 'eu'])->willReturn($resource); $form = $this->factory->create( ResourceAutocompleteChoiceType::class, null, ['resource' => 'sylius.zone', 'choice_name' => 'name', 'choice_value' => 'code'], ); $form->submit('eu'); $this->assertEquals('eu', $form->getViewData()); } /** * @test */ public function it_has_different_view_based_on_passed_configuration(): void { /** @var MockObject|RepositoryInterface $resourceRepository */ $resourceRepository = $this->createMock(RepositoryInterface::class); $resource = $this->createMock(ResourceInterface::class); $this->resourceRepositoryRegistry->method('get')->with('sylius.zone')->willReturn($resourceRepository); $resourceRepository->method('findOneBy')->with(['code' => 'eu'])->willReturn($resource); $form = $this->factory->create( ResourceAutocompleteChoiceType::class, null, ['resource' => 'sylius.zone', 'choice_name' => 'name', 'choice_value' => 'code'], ); $formViewVars = $form->createView()->vars; $this->assertSame('name', $formViewVars['choice_name']); $this->assertSame('code', $formViewVars['choice_value']); $this->assertFalse($formViewVars['multiple']); $this->assertSame('', $formViewVars['placeholder']); } /** * @test */ public function it_returns_collection_of_resources_from_identifiers(): void { /** @var MockObject|RepositoryInterface $resourceRepository */ $resourceRepository = $this->createMock(RepositoryInterface::class); $mug = $this->createMock(ResourceInterface::class); $book = $this->createMock(ResourceInterface::class); $sticker = $this->createMock(ResourceInterface::class); $this->resourceRepositoryRegistry->method('get')->with('sylius.resource')->willReturn($resourceRepository); $resourceRepository->method('findOneBy')->willReturnCallback(function (array $criteria) use ($mug, $book, $sticker) { if ($criteria === ['code' => 'mug']) { return $mug; } if ($criteria === ['code' => 'book']) { return $book; } if ($criteria === ['code' => 'sticker']) { return $sticker; } return null; }); $form = $this->factory->create( ResourceAutocompleteChoiceType::class, new ArrayCollection(), ['resource' => 'sylius.resource', 'choice_name' => 'name', 'choice_value' => 'code', 'multiple' => true], ); $form->submit('mug,book,sticker'); $this->assertEquals( new ArrayCollection([$mug, $book, $sticker]), $form->getData(), ); } /** * @test */ public function its_resource_option_should_be_string(): void { $this->expectException(InvalidOptionsException::class); $this->factory->create( ResourceAutocompleteChoiceType::class, new ArrayCollection(), ['resource' => 1, 'choice_name' => 'name', 'choice_value' => 'code', 'multiple' => true], ); } /** * @test */ public function its_choice_name_option_should_be_string(): void { $this->expectException(InvalidOptionsException::class); $this->factory->create( ResourceAutocompleteChoiceType::class, new ArrayCollection(), ['resource' => 1, 'choice_name' => 1, 'choice_value' => 'code', 'multiple' => true], ); } /** * @test */ public function its_choice_value_option_should_be_string(): void { $this->expectException(InvalidOptionsException::class); $this->factory->create( ResourceAutocompleteChoiceType::class, new ArrayCollection(), ['resource' => 'sylius.resource', 'choice_name' => 'name', 'choice_value' => 1, 'multiple' => true], ); } /** * @test */ public function its_multiple_option_should_be_boolean(): void { $this->expectException(InvalidOptionsException::class); $this->factory->create( ResourceAutocompleteChoiceType::class, new ArrayCollection(), ['resource' => 'sylius.resource', 'choice_name' => 'name', 'choice_value' => 'code', 'multiple' => 'yes'], ); } /** * @test */ public function its_placeholder_option_should_be_string(): void { $this->expectException(InvalidOptionsException::class); $this->factory->create( ResourceAutocompleteChoiceType::class, new ArrayCollection(), ['resource' => 'sylius.resource', 'choice_name' => 'name', 'choice_value' => 'code', 'placeholder' => 1], ); } /** * @test */ public function it_cannot_be_created_without_resource_option(): void { $this->expectException(MissingOptionsException::class); $this->factory->create( ResourceAutocompleteChoiceType::class, new ArrayCollection(), ['choice_name' => 'name', 'choice_value' => 'code'], ); } /** * @test */ public function it_cannot_be_created_without_choice_name_option(): void { $this->expectException(MissingOptionsException::class); $this->factory->create( ResourceAutocompleteChoiceType::class, new ArrayCollection(), ['resource' => 'sylius.resource', 'choice_value' => 'code'], ); } /** * @test */ public function it_cannot_be_created_without_choice_value_option(): void { $this->expectException(MissingOptionsException::class); $this->factory->create( ResourceAutocompleteChoiceType::class, new ArrayCollection(), ['resource' => 'sylius.resource', 'choice_name' => 'name'], ); } } ================================================ FILE: tests/Bundle/Form/Type/ResourceToIdentifierTypeTest.php ================================================ &MockObject */ private RepositoryInterface $repository; /** @var MetadataInterface&MockObject */ private MetadataInterface $metadata; private ResourceToIdentifierType $formType; protected function setUp(): void { $this->repository = $this->createMock(RepositoryInterface::class); $this->metadata = $this->createMock(MetadataInterface::class); $this->formType = new ResourceToIdentifierType($this->repository, $this->metadata); } public function testExtendsAbstractType(): void { self::assertInstanceOf(AbstractType::class, $this->formType); } public function testGetParent(): void { self::assertSame(EntityType::class, $this->formType->getParent()); } public function testGetBlockPrefix(): void { $this->metadata ->expects(self::once()) ->method('getApplicationName') ->willReturn('app'); $this->metadata ->expects(self::once()) ->method('getName') ->willReturn('product'); self::assertSame('app_product_to_identifier', $this->formType->getBlockPrefix()); } public function testConfigureOptionsWithDefaults(): void { $resolver = new OptionsResolver(); $this->formType->configureOptions($resolver); $options = $resolver->resolve(); self::assertArrayHasKey('identifier', $options); self::assertSame('id', $options['identifier']); } public function testConfigureOptionsWithCustomIdentifier(): void { $resolver = new OptionsResolver(); $this->formType->configureOptions($resolver); $options = $resolver->resolve(['identifier' => 'uuid']); self::assertSame('uuid', $options['identifier']); } public function testConfigureOptionsThrowsExceptionForInvalidIdentifierType(): void { $resolver = new OptionsResolver(); $this->formType->configureOptions($resolver); self::expectException(InvalidOptionsException::class); $resolver->resolve(['identifier' => 123]); } public function testBuildFormAddsResourceToIdentifierTransformer(): void { $builder = $this->createMock(FormBuilderInterface::class); $builder ->expects(self::once()) ->method('addModelTransformer') ->with(self::isInstanceOf(ResourceToIdentifierTransformer::class)); $this->formType->buildForm($builder, ['identifier' => 'id']); } public function testBuildFormAddsTransformerWithCustomIdentifier(): void { $builder = $this->createMock(FormBuilderInterface::class); $builder ->expects(self::once()) ->method('addModelTransformer') ->with(self::callback(function (ResourceToIdentifierTransformer $transformer): bool { return $transformer instanceof ResourceToIdentifierTransformer; })); $this->formType->buildForm($builder, ['identifier' => 'uuid']); } } ================================================ FILE: tests/Bundle/Form/Type/ResourceTranslationsTypeTest.php ================================================ localeProvider = $this->createLocaleProvider(self::DEFAULT_LOCALES, self::DEFAULT_LOCALE); $this->formType = new ResourceTranslationsType($this->localeProvider); } public function testExtendsAbstractType(): void { self::assertInstanceOf(AbstractType::class, $this->formType); } public function testGetParent(): void { self::assertSame(FixedCollectionType::class, $this->formType->getParent()); } public function testGetBlockPrefix(): void { self::assertSame('sylius_translations', $this->formType->getBlockPrefix()); } public function testConfigureOptionsWithDefaults(): void { $options = $this->resolveOptions(); self::assertArrayHasKey('entries', $options); self::assertArrayHasKey('entry_name', $options); self::assertArrayHasKey('entry_options', $options); self::assertSame(self::DEFAULT_LOCALES, $options['entries']); } public function testEntryNameCallableReturnsLocaleCode(): void { $options = $this->resolveOptions(); $entryName = $options['entry_name']; self::assertIsCallable($entryName); self::assertSame('en_US', $entryName('en_US')); self::assertSame('pl_PL', $entryName('pl_PL')); } public function testEntryOptionsCallableReturnsRequiredTrueForDefaultLocale(): void { $options = $this->resolveOptions(); $entryOptions = $options['entry_options']; self::assertIsCallable($entryOptions); $defaultLocaleOptions = $entryOptions(self::DEFAULT_LOCALE); self::assertArrayHasKey('required', $defaultLocaleOptions); self::assertTrue($defaultLocaleOptions['required']); } public function testEntryOptionsCallableReturnsRequiredFalseForNonDefaultLocale(): void { $options = $this->resolveOptions(); $entryOptions = $options['entry_options']; self::assertIsCallable($entryOptions); $nonDefaultLocaleOptions = $entryOptions('pl_PL'); self::assertArrayHasKey('required', $nonDefaultLocaleOptions); self::assertFalse($nonDefaultLocaleOptions['required']); } public function testBuildFormAddsSubmitEventListener(): void { $builder = $this->createMock(FormBuilderInterface::class); $builder ->expects(self::once()) ->method('addEventListener') ->with( self::identicalTo('form.submit'), self::callback(fn ($callback): bool => is_callable($callback)), ); $this->formType->buildForm($builder, []); } public function testUsesCustomLocalesFromProvider(): void { $customLocales = ['fr_FR', 'es_ES']; $customDefault = 'fr_FR'; $formType = new ResourceTranslationsType($this->createLocaleProvider($customLocales, $customDefault)); $options = $this->resolveOptions($formType); $entryOptions = $options['entry_options']; self::assertSame($customLocales, $options['entries']); self::assertIsCallable($entryOptions); $defaultLocaleOptions = $entryOptions($customDefault); self::assertTrue($defaultLocaleOptions['required']); $nonDefaultLocaleOptions = $entryOptions('es_ES'); self::assertFalse($nonDefaultLocaleOptions['required']); } public function testEventListenerSetsLocaleAndTranslatableOnTranslations(): void { $translatable = $this->createMock(TranslatableInterface::class); $translation1 = $this->createTranslationMock('en_US', $translatable); $translation2 = $this->createTranslationMock('pl_PL', $translatable); $translations = [ 'en_US' => $translation1, 'pl_PL' => $translation2, ]; $listener = $this->captureEventListener(); $event = $this->createFormEvent($translations, $translatable); $listener($event); self::assertSame($translations, $event->getData()); } public function testEventListenerRemovesNullTranslations(): void { $translatable = $this->createMock(TranslatableInterface::class); $translation = $this->createTranslationMock('en_US', $translatable); $translations = [ 'en_US' => $translation, 'pl_PL' => null, 'de_DE' => null, ]; $listener = $this->captureEventListener(); $event = $this->createFormEvent($translations, $translatable); $listener($event); $result = $event->getData(); self::assertIsArray($result); self::assertArrayHasKey('en_US', $result); self::assertArrayNotHasKey('pl_PL', $result); self::assertArrayNotHasKey('de_DE', $result); self::assertCount(1, $result); } public function testEventListenerHandlesEmptyTranslations(): void { $translatable = $this->createMock(TranslatableInterface::class); $listener = $this->captureEventListener(); $event = $this->createFormEvent([], $translatable); $listener($event); self::assertSame([], $event->getData()); } public function testEventListenerHandlesAllNullTranslations(): void { $translatable = $this->createMock(TranslatableInterface::class); $translations = [ 'en_US' => null, 'pl_PL' => null, ]; $listener = $this->captureEventListener(); $event = $this->createFormEvent($translations, $translatable); $listener($event); self::assertSame([], $event->getData()); } /** * @param string[] $locales * * @return TranslationLocaleProviderInterface&MockObject */ private function createLocaleProvider(array $locales, string $defaultLocale): TranslationLocaleProviderInterface { $provider = $this->createMock(TranslationLocaleProviderInterface::class); $provider->method('getDefinedLocalesCodes')->willReturn($locales); $provider->method('getDefaultLocaleCode')->willReturn($defaultLocale); return $provider; } /** @return array */ private function resolveOptions(?ResourceTranslationsType $formType = null): array { $resolver = new OptionsResolver(); ($formType ?? $this->formType)->configureOptions($resolver); return $resolver->resolve(); } /** @return TranslationInterface&MockObject */ private function createTranslationMock(string $locale, TranslatableInterface $translatable): TranslationInterface { $translation = $this->createMock(TranslationInterface::class); $translation->expects(self::once())->method('setLocale')->with($locale); $translation->expects(self::once())->method('setTranslatable')->with($translatable); return $translation; } private function captureEventListener(): callable { $listener = null; $builder = $this->createMock(FormBuilderInterface::class); $builder ->expects(self::once()) ->method('addEventListener') ->willReturnCallback(function ($eventName, $callback) use (&$listener, $builder) { $listener = $callback; return $builder; }); $this->formType->buildForm($builder, []); return $listener; } /** * @param array $translations */ private function createFormEvent(array $translations, TranslatableInterface $translatable): FormEvent { $parentForm = $this->createMock(FormInterface::class); $parentForm->method('getData')->willReturn($translatable); $form = $this->createMock(FormInterface::class); $form->method('getParent')->willReturn($parentForm); /** @var array|null $updatedData */ $updatedData = null; $event = $this->createMock(FormEvent::class); $event ->method('getData') ->willReturnCallback(function () use (&$updatedData, $translations) { return null !== $updatedData ? $updatedData : $translations; }); $event ->method('getForm') ->willReturn($form); $event ->expects(self::once()) ->method('setData') ->willReturnCallback(function ($data) use (&$updatedData): void { $updatedData = $data; }); return $event; } } ================================================ FILE: tests/Bundle/Grid/Controller/ResourcesResolverTest.php ================================================ &MockObject */ private RepositoryInterface $repository; protected function setUp(): void { $this->decoratedResolver = $this->createMock(ResourcesResolverInterface::class); $this->gridProvider = $this->createMock(GridProviderInterface::class); $this->gridViewFactory = $this->createMock(ResourceGridViewFactoryInterface::class); $this->resolver = new ResourcesResolver($this->decoratedResolver, $this->gridProvider, $this->gridViewFactory); $this->requestConfiguration = $this->createMock(RequestConfiguration::class); $this->repository = $this->createMock(RepositoryInterface::class); } public function testItImplementsResourcesResolverInterface(): void { $this->assertInstanceOf(ResourcesResolverInterface::class, $this->resolver); } public function testItUsesDecoratedResolverWhenNotUsingAGrid(): void { $resource = $this->createMock(ResourceInterface::class); $this->requestConfiguration->expects($this->once()) ->method('hasGrid') ->willReturn(false); $this->decoratedResolver->expects($this->once()) ->method('getResources') ->with($this->requestConfiguration, $this->repository) ->willReturn([$resource]); $result = $this->resolver->getResources($this->requestConfiguration, $this->repository); $this->assertSame([$resource], $result); } public function testItReturnsGridViewForHtmlRequest(): void { $gridView = $this->createMock(ResourceGridView::class); $this->configureGridRequest(true); $this->configureGridViewCreation($gridView); $result = $this->resolver->getResources($this->requestConfiguration, $this->repository); $this->assertSame($gridView, $result); } public function testItReturnsGridDataForNonHtmlRequest(): void { $gridView = $this->createMock(ResourceGridView::class); $paginator = $this->createMock(Pagerfanta::class); $this->configureGridRequest(false); $this->configureGridViewCreation($gridView); $gridView->expects($this->once()) ->method('getData') ->willReturn($paginator); $result = $this->resolver->getResources($this->requestConfiguration, $this->repository); $this->assertSame($paginator, $result); } private function configureGridRequest(bool $isHtmlRequest): void { $request = new Request(); $request->query = new InputBag(['foo' => 'bar']); $this->requestConfiguration->expects($this->once()) ->method('hasGrid') ->willReturn(true); $this->requestConfiguration->expects($this->once()) ->method('getGrid') ->willReturn('sylius_admin_tax_category'); $this->requestConfiguration->expects($this->once()) ->method('getMetadata') ->willReturn($this->createMock(MetadataInterface::class)); $this->requestConfiguration->expects($this->once()) ->method('isHtmlRequest') ->willReturn($isHtmlRequest); $this->requestConfiguration->expects($this->once()) ->method('getRequest') ->willReturn($request); } private function configureGridViewCreation(ResourceGridView $gridView): void { $gridDefinition = $this->createMock(Grid::class); $this->gridProvider->expects($this->once()) ->method('get') ->with('sylius_admin_tax_category') ->willReturn($gridDefinition); $this->gridViewFactory->expects($this->once()) ->method('create') ->with( $gridDefinition, $this->isInstanceOf(Parameters::class), $this->isInstanceOf(MetadataInterface::class), $this->requestConfiguration, ) ->willReturn($gridView); } } ================================================ FILE: tests/Bundle/Grid/Parser/OptionsParserTest.php ================================================ container = $this->createMock(ContainerInterface::class); $this->expression = $this->createMock(ExpressionLanguage::class); $this->propertyAccessor = $this->createMock(PropertyAccessorInterface::class); $this->parser = new OptionsParser($this->container, $this->expression, $this->propertyAccessor); } public function testItImplementsOptionsParserInterface(): void { $this->assertInstanceOf(OptionsParserInterface::class, $this->parser); } public function testItParsesOptionsWithRequestVariable(): void { $result = $this->parser->parseOptions(['id' => '$id'], new Request(attributes: ['id' => 7])); $this->assertSame(['id' => 7], $result); } public function testItReturnsNonStringParametersWithoutParsing(): void { $data = [ 'integer' => 10, 'float' => 19.99, 'true' => true, 'false' => false, 'null' => null, ]; $result = $this->parser->parseOptions($data, new Request()); $this->assertSame($data, $result); } public function testItParsesOptionsWithMixedTypes(): void { $result = $this->parser->parseOptions([ 'limit' => 25, 'enabled' => true, 'offset' => 0, 'price' => 99.99, 'optional' => null, 'status' => '$status', ], new Request(query: ['status' => 'active'])); $this->assertSame([ 'limit' => 25, 'enabled' => true, 'offset' => 0, 'price' => 99.99, 'optional' => null, 'status' => 'active', ], $result); } public function testItParsesOptionsWithExpression(): void { $this->configureExpressionEvaluation('service("demo_service")', 'demo_object'); $result = $this->parser->parseOptions( [ 'factory' => [ 'method' => 'createByParameter', 'arguments' => [ 'expr:service("demo_service")', ], ], ], new Request(), ); $this->assertSame( [ 'factory' => [ 'method' => 'createByParameter', 'arguments' => [ 'demo_object', ], ], ], $result, ); } public function testItParsesOptionsWithParameterFromResource(): void { $data = $this->createMock(ResourceInterface::class); $this->propertyAccessor->expects($this->once()) ->method('getValue') ->with($data, 'id') ->willReturn(21); $result = $this->parser->parseOptions(['id' => 'resource.id'], new Request(), $data); $this->assertSame(['id' => 21], $result); } public function testItParsesOptionsWithArrayResourceAccess(): void { $data = $this->createMock(ResourceInterface::class); $arrayData = [0 => $data, 'name' => 'An awesome name']; $this->propertyAccessor->expects($this->exactly(2)) ->method('getValue') ->willReturnCallback(fn ($subject, $path) => match ($path) { 'name' => 'An awesome name', '[0].id' => 21, default => null, }); $result = $this->parser->parseOptions(['name' => 'resource.name'], new Request(), $arrayData); $this->assertSame(['name' => 'An awesome name'], $result); $result = $this->parser->parseOptions(['id' => 'resource[0].id'], new Request(), $arrayData); $this->assertSame(['id' => 21], $result); } /** * @param array|null $requestVariables * @param array $options * @param array $expectedResult */ #[DataProvider('expressionVariableTypesProvider')] public function testItParsesExpressionWithVariableFromRequest( ?array $requestVariables, string $expectedExpression, array $options, array $expectedResult, ): void { $this->configureExpressionEvaluation($expectedExpression, reset($expectedResult)); $result = $this->parser->parseOptions($options, new Request(attributes: $requestVariables ?? [])); $this->assertSame($expectedResult, $result); } /** * @return iterable|null, string, array, array}> */ public static function expressionVariableTypesProvider(): iterable { yield 'string variable' => [ ['customerId' => 'user123'], 'service("customer_repository").find("user123")', ['customer' => 'expr:service("customer_repository").find($customerId)'], ['customer' => 'customer_object'], ]; yield 'integer variable' => [ ['productId' => 42], 'service("product_repository").find(42)', ['product' => 'expr:service("product_repository").find($productId)'], ['product' => 'product_object'], ]; yield 'null variable' => [ ['optionalParam' => null], 'service("some_service").process()', ['data' => 'expr:service("some_service").process($optionalParam)'], ['data' => 'result'], ]; yield 'string with special characters' => [ ['searchTerm' => 'user"test\\value'], 'service("search").find("user\"test\\\\value")', ['results' => 'expr:service("search").find($searchTerm)'], ['results' => 'search_results'], ]; yield 'multiple variables' => [ ['userId' => 10, 'limit' => 5], 'service("user_repository").findLatest(10, 5)', ['users' => 'expr:service("user_repository").findLatest($userId, $limit)'], ['users' => 'user_list'], ]; yield 'boolean variable' => [ ['enabled' => true], 'service("filter").apply(1)', ['data' => 'expr:service("filter").apply($enabled)'], ['data' => 'filtered_results'], ]; } private function configureExpressionEvaluation(string $expression, mixed $result): void { $this->expression->expects($this->once()) ->method('evaluate') ->with($expression, ['container' => $this->container]) ->willReturn($result); } } ================================================ FILE: tests/Bundle/Grid/Renderer/TwigBulkActionGridRendererTest.php ================================================ twig = $this->createMock(Environment::class); $this->optionsParser = $this->createMock(OptionsParserInterface::class); $this->renderer = new TwigBulkActionGridRenderer( $this->twig, $this->optionsParser, ['delete' => '@SyliusGrid/BulkAction/_delete.html.twig'], ); } public function testItImplementsBulkActionGridRendererInterface(): void { $this->assertInstanceOf(BulkActionGridRendererInterface::class, $this->renderer); } public function testItUsesTwigToRenderTheBulkAction(): void { $request = $this->createMock(Request::class); $requestConfiguration = $this->createConfiguredMock(RequestConfiguration::class, [ 'getRequest' => $request, ]); $gridView = $this->createConfiguredMock(ResourceGridView::class, [ 'getRequestConfiguration' => $requestConfiguration, ]); $bulkAction = $this->createBulkActionMock('delete', []); $this->configureOptionsParser([], $request); $this->configureTwigRenderer( '@SyliusGrid/BulkAction/_delete.html.twig', $gridView, $bulkAction, 'Delete', ); $result = $this->renderer->renderBulkAction($gridView, $bulkAction); $this->assertSame('Delete', $result); } public function testItThrowsAnExceptionIfTemplateIsNotConfiguredForGivenBulkActionType(): void { $gridView = $this->createMock(ResourceGridView::class); $bulkAction = $this->createBulkActionMock('foo'); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Missing template for bulk action type "foo".'); $this->renderer->renderBulkAction($gridView, $bulkAction); } /** * @param array $options */ private function createBulkActionMock(string $type, array $options = []): Action&MockObject { return $this->createConfiguredMock(Action::class, [ 'getType' => $type, 'getOptions' => $options, ]); } /** * @param array $options */ private function configureOptionsParser(array $options, Request $request): void { $this->optionsParser->expects($this->once()) ->method('parseOptions') ->with($options, $request, null); } private function configureTwigRenderer( string $template, ResourceGridView $gridView, Action $bulkAction, string $renderedContent, ): void { $this->twig->expects($this->once()) ->method('render') ->with($template, [ 'grid' => $gridView, 'action' => $bulkAction, 'data' => null, 'options' => [], ]) ->willReturn($renderedContent); } } ================================================ FILE: tests/Bundle/Grid/Renderer/TwigGridRendererTest.php ================================================ '@SyliusGrid/Action/_link.html.twig', 'form' => '@SyliusGrid/Action/_form.html.twig', ]; /** @var GridRendererInterface&MockObject */ private GridRendererInterface $gridRenderer; /** @var Environment&MockObject */ private Environment $twig; /** @var OptionsParserInterface&MockObject */ private OptionsParserInterface $optionsParser; private TwigGridRenderer $renderer; protected function setUp(): void { $this->gridRenderer = $this->createMock(GridRendererInterface::class); $this->twig = $this->createMock(Environment::class); $this->optionsParser = $this->createMock(OptionsParserInterface::class); $this->renderer = new TwigGridRenderer( $this->gridRenderer, $this->twig, $this->optionsParser, self::ACTION_TEMPLATES, ); } public function testItImplementsGridRendererInterface(): void { $this->assertInstanceOf(GridRendererInterface::class, $this->renderer); } public function testItUsesTwigToRenderTheAction(): void { $request = $this->createMock(Request::class); $requestConfiguration = $this->createConfiguredMock(RequestConfiguration::class, [ 'getRequest' => $request, ]); $gridView = $this->createConfiguredMock(ResourceGridView::class, [ 'getRequestConfiguration' => $requestConfiguration, ]); $action = $this->createActionMock('link', [], null); $this->configureOptionsParser([], $request); $this->configureTwigRenderer( '@SyliusGrid/Action/_link.html.twig', $gridView, $action, 'Action!', ); $result = $this->renderer->renderAction($gridView, $action); $this->assertSame('Action!', $result); } public function testItThrowsAnExceptionIfTemplateIsNotConfiguredForGivenActionType(): void { $gridView = $this->createMock(ResourceGridView::class); $action = $this->createActionMock('foo', [], null); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Missing template for action type "foo".'); $this->renderer->renderAction($gridView, $action); } public function testItCallsTheInnerRendererWithANonResourceGridView(): void { $gridView = $this->createMock(GridView::class); $action = $this->createMock(Action::class); $this->configureInnerRenderer($gridView, $action, 'foo'); $result = $this->renderer->renderAction($gridView, $action); $this->assertSame('foo', $result); } /** * @param array $options */ private function createActionMock(string $type, array $options = [], ?string $template = null): Action&MockObject { return $this->createConfiguredMock(Action::class, [ 'getType' => $type, 'getOptions' => $options, 'getTemplate' => $template, ]); } /** * @param array $options */ private function configureOptionsParser(array $options, Request $request): void { $this->optionsParser->expects($this->once()) ->method('parseOptions') ->with($options, $request, null); } private function configureTwigRenderer( string $template, ResourceGridView $gridView, Action $action, string $renderedContent, ): void { $this->twig->expects($this->once()) ->method('render') ->with($template, [ 'grid' => $gridView, 'action' => $action, 'data' => null, 'options' => [], ]) ->willReturn($renderedContent); } private function configureInnerRenderer(GridView $gridView, Action $action, string $result): void { $this->gridRenderer->expects($this->once()) ->method('renderAction') ->with($gridView, $action, null) ->willReturn($result); } } ================================================ FILE: tests/Bundle/Grid/View/LegacyGridViewFactoryTest.php ================================================ resourceGridViewFactory = $this->createMock(ResourceGridViewFactoryInterface::class); $this->decorated = $this->createMock(GridViewFactoryInterface::class); $this->factory = new LegacyGridViewFactory($this->resourceGridViewFactory, $this->decorated); } public function testItCreatesALegacyResourceGridView(): void { $grid = $this->createMock(Grid::class); $requestConfiguration = $this->createMock(RequestConfiguration::class); $metadata = $this->createMock(MetadataInterface::class); $resourceGridView = $this->createMock(ResourceGridView::class); $parameters = new Parameters(); $context = $this->createContextWithRequestConfiguration($requestConfiguration, $metadata); $this->configureResourceGridViewFactory($grid, $parameters, $metadata, $requestConfiguration, $resourceGridView); $result = $this->factory->create($grid, $context, $parameters, []); $this->assertSame($resourceGridView, $result); } public function testItCreatesAGridViewWhenContextHasNoRequestConfiguration(): void { $grid = $this->createMock(Grid::class); $metadata = $this->createMock(MetadataInterface::class); $gridView = $this->createMock(GridView::class); $parameters = new Parameters(); $context = $this->createContextWithoutRequestConfiguration($metadata); $this->configureDecoratedFactory($grid, $context, $parameters, $gridView); $result = $this->factory->create($grid, $context, $parameters, []); $this->assertSame($gridView, $result); } private function createContextWithRequestConfiguration( RequestConfiguration $requestConfiguration, MetadataInterface $metadata, ): Context { return new Context( new RequestConfigurationOption($requestConfiguration), new MetadataOption($metadata), ); } private function createContextWithoutRequestConfiguration(MetadataInterface $metadata): Context { return new Context( new MetadataOption($metadata), ); } private function configureResourceGridViewFactory( Grid $grid, Parameters $parameters, MetadataInterface $metadata, RequestConfiguration $requestConfiguration, ResourceGridView $resourceGridView, ): void { $this->resourceGridViewFactory->expects($this->once()) ->method('create') ->with($grid, $parameters, $metadata, $requestConfiguration) ->willReturn($resourceGridView); } private function configureDecoratedFactory( Grid $grid, Context $context, Parameters $parameters, GridView $gridView, ): void { $this->decorated->expects($this->once()) ->method('create') ->with($grid, $context, $parameters, []) ->willReturn($gridView); } } ================================================ FILE: tests/Bundle/Grid/View/ResourceGridViewFactoryTest.php ================================================ [ 'method' => 'createByCustomerQueryBuilder', 'arguments' => ['$customerId'], ], ]; private const DRIVER_CONFIG_AFTER = [ 'repository' => [ 'method' => 'createByCustomerQueryBuilder', 'arguments' => [5], ], ]; private const GRID_DATA = ['foo', 'bar']; /** @var DataProviderInterface&MockObject */ private DataProviderInterface $dataProvider; /** @var ParametersParserInterface&MockObject */ private ParametersParserInterface $parametersParser; private ResourceGridViewFactory $factory; protected function setUp(): void { $this->dataProvider = $this->createMock(DataProviderInterface::class); $this->parametersParser = $this->createMock(ParametersParserInterface::class); $this->factory = new ResourceGridViewFactory($this->dataProvider, $this->parametersParser); } public function testItImplementsResourceGridViewFactoryInterface(): void { $this->assertInstanceOf(ResourceGridViewFactoryInterface::class, $this->factory); } public function testItUsesDataProviderToCreateAViewWithDataAndDefinition(): void { $parameters = new Parameters(); $resourceMetadata = $this->createMock(MetadataInterface::class); $request = $this->createMock(Request::class); $requestConfiguration = $this->createConfiguredMock(RequestConfiguration::class, [ 'getRequest' => $request, ]); $grid = $this->createGridMock(); $this->configureParametersParser($request); $this->configureDataProvider($grid, $parameters); $result = $this->factory->create($grid, $parameters, $resourceMetadata, $requestConfiguration); $this->assertResourceGridView($result, $grid, $parameters, $resourceMetadata, $requestConfiguration); } private function createGridMock(): Grid&MockObject { return $this->createConfiguredMock(Grid::class, [ 'getDriverConfiguration' => self::DRIVER_CONFIG_BEFORE, ]); } private function configureParametersParser(Request $request): void { $this->parametersParser->expects($this->once()) ->method('parseRequestValues') ->with(self::DRIVER_CONFIG_BEFORE, $request) ->willReturn(self::DRIVER_CONFIG_AFTER); } private function configureDataProvider(Grid&MockObject $grid, Parameters $parameters): void { /** @var MockObject $gridMock */ $gridMock = $grid; $gridMock->expects($this->once()) ->method('setDriverConfiguration') ->with(self::DRIVER_CONFIG_AFTER); $this->dataProvider->expects($this->once()) ->method('getData') ->with($grid, $parameters) ->willReturn(self::GRID_DATA); } private function assertResourceGridView( ResourceGridView $result, Grid $grid, Parameters $parameters, MetadataInterface $resourceMetadata, RequestConfiguration $requestConfiguration, ): void { $this->assertInstanceOf(ResourceGridView::class, $result); $this->assertSame(self::GRID_DATA, $result->getData()); $this->assertSame($grid, $result->getDefinition()); $this->assertSame($parameters, $result->getParameters()); $this->assertSame($resourceMetadata, $result->getMetadata()); $this->assertSame($requestConfiguration, $result->getRequestConfiguration()); } } ================================================ FILE: tests/Bundle/Grid/View/ResourceGridViewTest.php ================================================ resourceMetadata = $this->createMock(MetadataInterface::class); $this->requestConfiguration = $this->createMock(RequestConfiguration::class); $this->gridView = $this->createResourceGridView(); } public function testItExtendsDefaultGridView(): void { $this->assertInstanceOf(GridView::class, $this->gridView); } public function testItHasResourceMetadata(): void { $this->assertSame($this->resourceMetadata, $this->gridView->getMetadata()); } public function testItHasRequestConfiguration(): void { $this->assertSame($this->requestConfiguration, $this->gridView->getRequestConfiguration()); } private function createResourceGridView(): ResourceGridView { return new ResourceGridView( self::GRID_DATA, $this->createMock(Grid::class), new Parameters(), $this->resourceMetadata, $this->requestConfiguration, ); } } ================================================ FILE: tests/Bundle/Provider/RequestParameterProviderTest.php ================================================ 'bar_query'], ['foo' => 'bar_request'], ['foo' => 'bar_attribute']); $this->assertSame(RequestParameterProvider::provide($request, 'foo'), 'bar_attribute'); } /** @test */ public function it_returns_request_query_parameter_value(): void { $request = new Request(['foo' => 'bar_query'], ['foo' => 'bar_request'], ['foo_attribute' => 'bar_attribute']); $this->assertSame(RequestParameterProvider::provide($request, 'foo'), 'bar_query'); } /** @test */ public function it_returns_request_request_parameter_value(): void { $request = new Request(['foo_query' => 'bar_query'], ['foo' => 'bar_request'], ['foo_attribute' => 'bar_attribute']); $this->assertSame(RequestParameterProvider::provide($request, 'foo'), 'bar_request'); } /** @test */ public function it_returns_null_or_default_value_if_parameter_is_not_set(): void { $request = new Request(['foo_query' => 'bar_query'], ['foo_request' => 'bar_request'], ['foo_attribute' => 'bar_attribute']); $this->assertSame(RequestParameterProvider::provide($request, 'foo'), null); $this->assertSame(RequestParameterProvider::provide($request, 'foo', 'default'), 'default'); } } ================================================ FILE: tests/Bundle/Resource/ResourceServicesTest.php ================================================ getContainer()->get('app.repository.book'); $this->assertInstanceOf(RepositoryInterface::class, $repository); $repository = $client->getContainer()->get('app.repository.comic_book'); $this->assertInstanceOf(ServiceEntityRepository::class, $repository); $manager = $client->getContainer()->get('app.manager.book'); $this->assertInstanceOf(EntityManager::class, $manager); $controller = $client->getContainer()->get('app.controller.book'); $this->assertInstanceOf(ResourceController::class, $controller); $factory = $client->getContainer()->get('app.factory.book'); $this->assertInstanceOf(FactoryInterface::class, $factory); } /** * @test */ public function it_will_return_the_same_repository_instance() { $client = parent::createClient(); $repository = self::getContainer()->get(BookRepository::class); $repositoryAlias = $client->getContainer()->get('app.repository.book'); $this->assertSame($repository, $repositoryAlias); $em = $client->getContainer()->get('app.manager.book'); $repositoryAlias = $em->getRepository(Book::class); $this->assertSame($repository, $repositoryAlias); $em = $client->getContainer()->get('doctrine.orm.entity_manager'); $repositoryAlias = $em->getRepository(Book::class); $this->assertSame($repository, $repositoryAlias); } /** * @test */ public function it_will_return_the_same_repository_instance_for_default_repositories() { $client = parent::createClient(); $em = $client->getContainer()->get('doctrine.orm.entity_manager'); $repository = $client->getContainer()->get('app.repository.comic_book'); $repositoryAlias = $em->getRepository(ComicBook::class); $this->assertInstanceOf(RepositoryInterface::class, $repository); $this->assertSame($repository, $repositoryAlias); } } ================================================ FILE: tests/Bundle/Routing/CrudRoutesAttributesLoaderTest.php ================================================ 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); // Test index $bookIndex = $routesCollection->get('app_book_index'); $this->assertNotNull($bookIndex); $this->assertEquals($this->isRoutingPathBcLayerEnabled() ? '/books/' : '/books', $bookIndex->getPath()); $this->assertEquals(['GET'], $bookIndex->getMethods()); $this->assertEquals([ '_controller' => 'app.controller.book::indexAction', '_sylius' => [ 'permission' => false, ], ], $bookIndex->getDefaults()); // Test create $bookCreate = $routesCollection->get('app_book_create'); $this->assertNotNull($bookCreate); $this->assertEquals('/books/new', $bookCreate->getPath()); $this->assertEquals(['GET', 'POST'], $bookCreate->getMethods()); $this->assertEquals([ '_controller' => 'app.controller.book::createAction', '_sylius' => [ 'permission' => false, ], ], $bookCreate->getDefaults()); // Test show $bookShow = $routesCollection->get('app_book_show'); $this->assertNotNull($bookShow); $this->assertEquals('/books/{id}', $bookShow->getPath()); $this->assertEquals(['GET'], $bookShow->getMethods()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'permission' => false, ], ], $bookShow->getDefaults()); // Test update $bookUpdate = $routesCollection->get('app_book_update'); $this->assertNotNull($bookUpdate); $this->assertEquals($this->isRoutingPathBcLayerEnabled() ? ['GET', 'PUT', 'PATCH'] : ['GET', 'PUT', 'PATCH', 'POST'], $bookUpdate->getMethods()); $this->assertEquals('/books/{id}/edit', $bookUpdate->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::updateAction', '_sylius' => [ 'permission' => false, ], ], $bookUpdate->getDefaults()); // Test delete $bookDelete = $routesCollection->get('app_book_delete'); $this->assertNotNull($bookDelete); $this->assertEquals($this->isRoutingPathBcLayerEnabled() ? '/books/{id}' : '/books/{id}/delete', $bookDelete->getPath()); $this->assertEquals($this->isRoutingPathBcLayerEnabled() ? ['DELETE'] : ['DELETE', 'POST'], $bookDelete->getMethods()); $this->assertEquals([ '_controller' => 'app.controller.book::deleteAction', '_sylius' => [ 'permission' => false, ], ], $bookDelete->getDefaults()); } /** * @test */ public function it_generates_crud_routes_from_resource_with_section(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); // Test index $bookIndex = $routesCollection->get('app_admin_book_index'); $this->assertNotNull($bookIndex); // Test create $bookCreate = $routesCollection->get('app_admin_book_create'); $this->assertNotNull($bookCreate); // Test show $bookShow = $routesCollection->get('app_admin_book_show'); $this->assertNotNull($bookShow); // Test update $bookUpdate = $routesCollection->get('app_admin_book_update'); $this->assertNotNull($bookUpdate); // Test delete $bookDelete = $routesCollection->get('app_admin_book_delete'); $this->assertNotNull($bookDelete); } /** * @test */ public function it_generates_crud_routes_from_resource_with_criteria(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $section = 'criteria'; // Test index $bookIndex = $routesCollection->get(sprintf('app_%s_book_index', $section)); $this->assertNotNull($bookIndex); $this->assertEquals([ 'permission' => false, 'section' => $section, 'criteria' => [ 'library' => '$libraryId', ], ], $bookIndex->getDefault('_sylius')); // Test create $bookCreate = $routesCollection->get(sprintf('app_%s_book_create', $section)); $this->assertNotNull($bookCreate); $this->assertEquals([ 'permission' => false, 'section' => $section, 'criteria' => [ 'library' => '$libraryId', ], ], $bookCreate->getDefault('_sylius')); // Test show $bookShow = $routesCollection->get(sprintf('app_%s_book_show', $section)); $this->assertNotNull($bookShow); $this->assertEquals([ 'permission' => false, 'section' => $section, 'criteria' => [ 'library' => '$libraryId', ], ], $bookShow->getDefault('_sylius')); // Test update $bookUpdate = $routesCollection->get(sprintf('app_%s_book_update', $section)); $this->assertNotNull($bookUpdate); $this->assertEquals([ 'permission' => false, 'section' => $section, 'criteria' => [ 'library' => '$libraryId', ], ], $bookUpdate->getDefault('_sylius')); // Test delete $bookDelete = $routesCollection->get(sprintf('app_%s_book_delete', $section)); $this->assertNotNull($bookDelete); $this->assertEquals([ 'permission' => false, 'section' => $section, 'criteria' => [ 'library' => '$libraryId', ], ], $bookDelete->getDefault('_sylius')); } /** * @test */ public function it_generates_crud_routes_from_resource_with_templates(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $section = 'template'; // Test index $bookIndex = $routesCollection->get(sprintf('app_%s_book_index', $section)); $this->assertNotNull($bookIndex); $this->assertEquals([ 'permission' => false, 'section' => $section, 'template' => 'backend/crud/index.html.twig', ], $bookIndex->getDefault('_sylius')); // Test create $bookCreate = $routesCollection->get(sprintf('app_%s_book_create', $section)); $this->assertNotNull($bookCreate); $this->assertEquals([ 'permission' => false, 'section' => $section, 'template' => 'backend/crud/create.html.twig', ], $bookCreate->getDefault('_sylius')); // Test show $bookShow = $routesCollection->get(sprintf('app_%s_book_show', $section)); $this->assertNotNull($bookShow); $this->assertEquals([ 'permission' => false, 'section' => $section, 'template' => 'backend/crud/show.html.twig', ], $bookShow->getDefault('_sylius')); // Test update $bookUpdate = $routesCollection->get(sprintf('app_%s_book_update', $section)); $this->assertNotNull($bookUpdate); $this->assertEquals([ 'permission' => false, 'section' => $section, 'template' => 'backend/crud/update.html.twig', ], $bookUpdate->getDefault('_sylius')); // Test delete $bookDelete = $routesCollection->get(sprintf('app_%s_book_delete', $section)); $this->assertNotNull($bookDelete); $this->assertEquals([ 'permission' => false, 'section' => $section, ], $bookDelete->getDefault('_sylius')); } /** * @test */ public function it_generates_crud_routes_from_resource_with_grids(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $section = 'grid'; // Test index $bookIndex = $routesCollection->get(sprintf('app_%s_book_index', $section)); $this->assertNotNull($bookIndex); $this->assertEquals([ 'permission' => false, 'section' => $section, 'grid' => 'app_book', ], $bookIndex->getDefault('_sylius')); // Test create $bookCreate = $routesCollection->get(sprintf('app_%s_book_create', $section)); $this->assertNotNull($bookCreate); $this->assertEquals([ 'permission' => false, 'section' => $section, ], $bookCreate->getDefault('_sylius')); // Test show $bookShow = $routesCollection->get(sprintf('app_%s_book_show', $section)); $this->assertNotNull($bookShow); $this->assertEquals([ 'permission' => false, 'section' => $section, ], $bookShow->getDefault('_sylius')); // Test update $bookUpdate = $routesCollection->get(sprintf('app_%s_book_update', $section)); $this->assertNotNull($bookUpdate); $this->assertEquals([ 'permission' => false, 'section' => $section, ], $bookUpdate->getDefault('_sylius')); // Test delete $bookDelete = $routesCollection->get(sprintf('app_%s_book_delete', $section)); $this->assertNotNull($bookDelete); $this->assertEquals([ 'permission' => false, 'section' => $section, ], $bookDelete->getDefault('_sylius')); } /** * @test */ public function it_generates_crud_routes_from_resource_with_vars(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $section = 'vars'; // Test index $bookIndex = $routesCollection->get(sprintf('app_%s_book_index', $section)); $this->assertNotNull($bookIndex); $this->assertEquals([ 'permission' => false, 'section' => $section, 'vars' => [ 'subheader' => 'app.ui.manage_your_books', 'icon' => 'book', ], ], $bookIndex->getDefault('_sylius')); // Test create $bookCreate = $routesCollection->get(sprintf('app_%s_book_create', $section)); $this->assertNotNull($bookCreate); $this->assertEquals([ 'permission' => false, 'section' => $section, 'vars' => [ 'subheader' => 'app.ui.manage_your_books', ], ], $bookCreate->getDefault('_sylius')); // Test show $bookShow = $routesCollection->get(sprintf('app_%s_book_show', $section)); $this->assertNotNull($bookShow); $this->assertEquals([ 'permission' => false, 'section' => $section, 'vars' => [ 'subheader' => 'app.ui.manage_your_books', ], ], $bookShow->getDefault('_sylius')); // Test update $bookUpdate = $routesCollection->get(sprintf('app_%s_book_update', $section)); $this->assertNotNull($bookUpdate); $this->assertEquals([ 'permission' => false, 'section' => $section, 'vars' => [ 'subheader' => 'app.ui.manage_your_books', ], ], $bookUpdate->getDefault('_sylius')); // Test delete $bookDelete = $routesCollection->get(sprintf('app_%s_book_delete', $section)); $this->assertNotNull($bookDelete); $this->assertEquals([ 'permission' => false, 'section' => $section, 'vars' => [ 'subheader' => 'app.ui.manage_your_books', ], ], $bookDelete->getDefault('_sylius')); } /** * @test */ public function it_generates_crud_routes_from_resource_with_redirect(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $section = 'redirect'; // Test index $bookIndex = $routesCollection->get(sprintf('app_%s_book_index', $section)); $this->assertNotNull($bookIndex); $this->assertEquals([ 'permission' => false, 'section' => $section, ], $bookIndex->getDefault('_sylius')); // Test create $bookCreate = $routesCollection->get(sprintf('app_%s_book_create', $section)); $this->assertNotNull($bookCreate); $this->assertEquals([ 'permission' => false, 'section' => $section, 'redirect' => 'app_redirect_book_update', ], $bookCreate->getDefault('_sylius')); // Test show $bookShow = $routesCollection->get(sprintf('app_%s_book_show', $section)); $this->assertNotNull($bookShow); $this->assertEquals([ 'permission' => false, 'section' => $section, ], $bookShow->getDefault('_sylius')); // Test update $bookUpdate = $routesCollection->get(sprintf('app_%s_book_update', $section)); $this->assertNotNull($bookUpdate); $this->assertEquals([ 'permission' => false, 'section' => $section, 'redirect' => 'app_redirect_book_update', ], $bookUpdate->getDefault('_sylius')); // Test delete $bookDelete = $routesCollection->get(sprintf('app_%s_book_delete', $section)); $this->assertNotNull($bookDelete); $this->assertEquals([ 'permission' => false, 'section' => $section, ], $bookDelete->getDefault('_sylius')); } /** * @test */ public function it_generates_crud_routes_from_resource_with_permission(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $section = 'permission'; // Test index $bookIndex = $routesCollection->get(sprintf('app_%s_book_index', $section)); $this->assertNotNull($bookIndex); $this->assertEquals([ 'permission' => true, 'section' => $section, ], $bookIndex->getDefault('_sylius')); // Test create $bookCreate = $routesCollection->get(sprintf('app_%s_book_create', $section)); $this->assertNotNull($bookCreate); $this->assertEquals([ 'permission' => true, 'section' => $section, ], $bookCreate->getDefault('_sylius')); // Test show $bookShow = $routesCollection->get(sprintf('app_%s_book_show', $section)); $this->assertNotNull($bookShow); $this->assertEquals([ 'permission' => true, 'section' => $section, ], $bookShow->getDefault('_sylius')); // Test update $bookUpdate = $routesCollection->get(sprintf('app_%s_book_update', $section)); $this->assertNotNull($bookUpdate); $this->assertEquals([ 'permission' => true, 'section' => $section, ], $bookUpdate->getDefault('_sylius')); // Test delete $bookDelete = $routesCollection->get('app_permission_book_delete'); $this->assertNotNull($bookDelete); $this->assertEquals([ 'permission' => true, 'section' => $section, ], $bookDelete->getDefault('_sylius')); } /** * @test */ public function it_generates_crud_routes_from_resource_with_except(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $section = 'except'; // Test create $bookCreate = $routesCollection->get(sprintf('app_%s_book_create', $section)); $this->assertNull($bookCreate); // Test show $bookShow = $routesCollection->get(sprintf('app_%s_book_show', $section)); $this->assertNull($bookShow); // Test index $bookIndex = $routesCollection->get(sprintf('app_%s_book_index', $section)); $this->assertNotNull($bookIndex); // Test update $bookUpdate = $routesCollection->get(sprintf('app_%s_book_update', $section)); $this->assertNotNull($bookUpdate); // Test delete $bookDelete = $routesCollection->get(sprintf('app_%s_book_delete', $section)); $this->assertNotNull($bookDelete); } /** * @test */ public function it_generates_crud_routes_from_resource_with_only(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $section = 'only'; // Test index $bookIndex = $routesCollection->get(sprintf('app_%s_book_index', $section)); $this->assertNotNull($bookIndex); // Test update $bookUpdate = $routesCollection->get(sprintf('app_%s_book_update', $section)); $this->assertNotNull($bookUpdate); // Test show $bookShow = $routesCollection->get(sprintf('app_%s_book_show', $section)); $this->assertNull($bookShow); // Test delete $bookDelete = $routesCollection->get(sprintf('app_%s_book_delete', $section)); $this->assertNull($bookDelete); // Test create $bookCreate = $routesCollection->get(sprintf('app_%s_book_create', $section)); $this->assertNull($bookCreate); } /** * @test */ public function it_generates_crud_routes_from_resource_with_legacy_attribute(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.crud_routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $section = 'legacy_attribute'; // Test index $bookIndex = $routesCollection->get(sprintf('app_%s_book_index', $section)); $this->assertNotNull($bookIndex); // Test update $bookUpdate = $routesCollection->get(sprintf('app_%s_book_update', $section)); $this->assertNotNull($bookUpdate); // Test show $bookShow = $routesCollection->get(sprintf('app_%s_book_show', $section)); $this->assertNotNull($bookShow); // Test delete $bookDelete = $routesCollection->get(sprintf('app_%s_book_delete', $section)); $this->assertNotNull($bookDelete); // Test create $bookCreate = $routesCollection->get(sprintf('app_%s_book_create', $section)); $this->assertNotNull($bookCreate); } private function isRoutingPathBcLayerEnabled(): bool { return (bool) $this->getContainer()->getParameter('sylius.routing_path_bc_layer'); } } ================================================ FILE: tests/Bundle/Routing/ResourceLoaderTest.php ================================================ createLoader(); $this->assertInstanceOf(LoaderInterface::class, $loader); } public function testItThrowsExceptionWhenBothOnlyAndExceptOptionsAreConfigured(): void { $loader = $this->createLoader(); $resource = <<expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('You can configure only one of "except" & "only" options.'); $loader->load($resource, 'sylius.resource'); } public function testItSupportsSyliusResourceType(): void { $loader = $this->createLoader(); $this->assertTrue($loader->supports('resource', 'sylius.resource')); } public function testItSupportsSyliusResourceApiType(): void { $loader = $this->createLoader(); $this->assertTrue($loader->supports('resource', 'sylius.resource_api')); } public function testItDoesNotSupportOtherTypes(): void { $loader = $this->createLoader(); $this->assertFalse($loader->supports('resource', 'other_type')); } public function testItGeneratesRoutingBasedOnResourceConfiguration(): void { $loader = $this->createLoaderWithMockedDependencies(); $resource = <<load($resource, 'sylius.resource'); $this->assertInstanceOf(RouteCollection::class, $result); $this->assertNotNull($result->get('sylius_product_index')); $this->assertNotNull($result->get('sylius_product_show')); $this->assertNotNull($result->get('sylius_product_create')); $this->assertNotNull($result->get('sylius_product_update')); $this->assertNotNull($result->get('sylius_product_delete')); $this->assertNotNull($result->get('sylius_product_bulk_delete')); } public function testItIncludesOnlySpecificRoutesIfConfigured(): void { $loader = $this->createLoaderWithMockedDependencies(); $resource = <<load($resource, 'sylius.resource'); $this->assertNotNull($result->get('sylius_product_index')); $this->assertNotNull($result->get('sylius_product_create')); $this->assertNull($result->get('sylius_product_show')); $this->assertNull($result->get('sylius_product_update')); $this->assertNull($result->get('sylius_product_delete')); $this->assertNull($result->get('sylius_product_bulk_delete')); } public function testItExcludesSpecificRoutesIfConfigured(): void { $loader = $this->createLoaderWithMockedDependencies(); $resource = <<load($resource, 'sylius.resource'); $this->assertNotNull($result->get('sylius_product_index')); $this->assertNotNull($result->get('sylius_product_create')); $this->assertNotNull($result->get('sylius_product_update')); $this->assertNull($result->get('sylius_product_show')); $this->assertNull($result->get('sylius_product_delete')); $this->assertNull($result->get('sylius_product_bulk_delete')); } public function testItGeneratesApiRoutingWithoutBulkDelete(): void { $loader = $this->createLoaderWithMockedDependencies(); $resource = <<load($resource, 'sylius.resource_api'); $this->assertNotNull($result->get('sylius_product_index')); $this->assertNotNull($result->get('sylius_product_show')); $this->assertNotNull($result->get('sylius_product_create')); $this->assertNotNull($result->get('sylius_product_update')); $this->assertNotNull($result->get('sylius_product_delete')); $this->assertNull($result->get('sylius_product_bulk_delete')); } public function testItGeneratesRoutingWithCustomFormConfiguration(): void { $loader = $this->createLoaderWithMockedDependencies(); $resource = <<load($resource, 'sylius.resource'); $this->assertRouteHasSyliusOption($result->get('sylius_product_create'), 'form', 'sylius_product_custom'); $this->assertRouteHasSyliusOption($result->get('sylius_product_update'), 'form', 'sylius_product_custom'); } public function testItGeneratesRoutingWithSerializationVersion(): void { $loader = $this->createLoaderWithMockedDependencies(); $resource = <<load($resource, 'sylius.resource'); $this->assertRouteHasSyliusOption($result->get('sylius_product_index'), 'serialization_version', '1.0'); $this->assertRouteHasSyliusOption($result->get('sylius_product_show'), 'serialization_version', '1.0'); } public function testItGeneratesRoutingWithFilterableOption(): void { $loader = $this->createLoaderWithMockedDependencies(); $resource = <<load($resource, 'sylius.resource'); $this->assertRouteHasSyliusOption($result->get('sylius_product_index'), 'filterable', true); } public function testItGeneratesRoutingWithFilterableFalse(): void { $loader = $this->createLoaderWithMockedDependencies(); $resource = <<load($resource, 'sylius.resource'); $this->assertRouteHasSyliusOption($result->get('sylius_product_index'), 'filterable', false); } public function testItGeneratesRoutingWithBcLayerDisabled(): void { $loader = $this->createLoaderWithMockedDependencies(false); $resource = <<load($resource, 'sylius.resource'); $deleteRoute = $result->get('sylius_product_delete'); $this->assertNotNull($deleteRoute); $this->assertContains('DELETE', $deleteRoute->getMethods()); $this->assertContains('POST', $deleteRoute->getMethods()); $this->assertEquals('/products/{id}/delete', $deleteRoute->getPath()); } public function testItGeneratesRoutingWithBcLayerEnabled(): void { $this->markAsSkippedIfBcLayerCannotBeEnabled(); $loader = $this->createLoaderWithMockedDependencies(true); $resource = <<load($resource, 'sylius.resource'); $deleteRoute = $result->get('sylius_product_delete'); $this->assertNotNull($deleteRoute); $this->assertEquals(['DELETE'], $deleteRoute->getMethods()); $this->assertNotContains('POST', $deleteRoute->getMethods()); $this->assertEquals('/products/{id}', $deleteRoute->getPath()); } public function testItGeneratesBulkDeleteRoutingWithBcLayerDisabled(): void { $loader = $this->createLoaderWithMockedDependencies(false); $resource = <<load($resource, 'sylius.resource'); $bulkDeleteRoute = $result->get('sylius_product_bulk_delete'); $this->assertNotNull($bulkDeleteRoute); $this->assertContains('DELETE', $bulkDeleteRoute->getMethods()); $this->assertContains('POST', $bulkDeleteRoute->getMethods()); $this->assertEquals('/products/bulk-delete', $bulkDeleteRoute->getPath()); } public function testItGeneratesBulkDeleteRoutingWithBcLayerEnabled(): void { $this->markAsSkippedIfBcLayerCannotBeEnabled(); $loader = $this->createLoaderWithMockedDependencies(true); $resource = <<load($resource, 'sylius.resource'); $bulkDeleteRoute = $result->get('sylius_product_bulk_delete'); $this->assertNotNull($bulkDeleteRoute); $this->assertEquals(['DELETE'], $bulkDeleteRoute->getMethods()); $this->assertNotContains('POST', $bulkDeleteRoute->getMethods()); $this->assertEquals('/products/bulk-delete', $bulkDeleteRoute->getPath()); } public function testItGeneratesUpdateRoutingWithBcLayerDisabled(): void { $loader = $this->createLoaderWithMockedDependencies(false); $resource = <<load($resource, 'sylius.resource'); $updateRoute = $result->get('sylius_product_update'); $this->assertNotNull($updateRoute); $this->assertContains('GET', $updateRoute->getMethods()); $this->assertContains('PUT', $updateRoute->getMethods()); $this->assertContains('PATCH', $updateRoute->getMethods()); $this->assertContains('POST', $updateRoute->getMethods()); } private function createLoader(?bool $bcLayerEnabled = null): ResourceLoader { $registry = $this->createMock(RegistryInterface::class); $routeFactory = $this->createMock(RouteFactoryInterface::class); return new ResourceLoader($registry, $routeFactory, null, $bcLayerEnabled); } private function createLoaderWithMockedDependencies(?bool $bcLayerEnabled = null): ResourceLoader { $registry = $this->createMock(RegistryInterface::class); $metadata = $this->createMock(MetadataInterface::class); $routeFactory = $this->createMock(RouteFactoryInterface::class); $routeCollection = new RouteCollection(); $metadata->method('getApplicationName')->willReturn('sylius'); $metadata->method('getName')->willReturn('product'); $metadata->method('getPluralName')->willReturn('products'); $metadata->method('getServiceId')->willReturn('sylius.controller.product'); $registry->method('get')->with('sylius.product')->willReturn($metadata); $routeFactory->method('createRouteCollection')->willReturn($routeCollection); $routeFactory->method('createRoute')->willReturnCallback( fn (string $path, array $defaults, array $requirements = [], array $options = [], string $host = '', array $schemes = [], array $methods = []) => new Route($path, $defaults, $requirements, $options, $host, $schemes, $methods), ); return new ResourceLoader($registry, $routeFactory, null, $bcLayerEnabled); } private function assertRouteHasSyliusOption(?Route $route, string $key, mixed $expectedValue): void { $this->assertNotNull($route); $this->assertArrayHasKey('_sylius', $route->getDefaults()); $this->assertArrayHasKey($key, $route->getDefaults()['_sylius']); $this->assertEquals($expectedValue, $route->getDefaults()['_sylius'][$key]); } private function markAsSkippedIfBcLayerCannotBeEnabled(): void { if (!class_exists(Transliterator::class)) { $this->markTestSkipped('This test requires The Behat Transliterator.'); } } } ================================================ FILE: tests/Bundle/Routing/RoutesAttributesLoaderTest.php ================================================ 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_methods(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_methods'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals(['GET'], $route->getMethods()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_criteria(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_criteria'); $this->assertNotNull($route); $this->assertEquals('/library/{libraryId}/book/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'criteria' => [ 'library' => '$libraryId', ], ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_template(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_template'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'template' => 'book/show.html.twig', ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_repository(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_repository'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'repository' => [ 'method' => 'findOneNewestByAuthor', 'arguments' => '[$author]', ], ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_serialization_groups(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_serialization_groups'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'serialization_groups' => ['sylius'], ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_serialization_version(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_serialization_version'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'serialization_version' => '1.0', ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_vars(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_vars'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'vars' => [ 'foo' => 'bar', ], ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_requirements(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_requirements'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals([ 'id' => '\d+', ], $route->getRequirements()); } /** @test */ public function it_generates_routes_from_resource_with_priority(): void { if (Kernel::MAJOR_VERSION < 5) { $this->markTestSkipped(); } self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_priority'); $this->assertNotNull($route); $this->assertSame($route, array_values($routesCollection->all())[0]); } /** @test */ public function it_generates_routes_from_resource_with_options(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_options'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals([ 'compiler_class' => RouteCompiler::class, 'utf8' => true, ], $route->getOptions()); } /** @test */ public function it_generates_routes_from_resource_with_host(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_host'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals('m.example.com', $route->getHost()); } /** @test */ public function it_generates_routes_from_resource_with_schemes(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_schemes'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals(['https'], $route->getSchemes()); } /** @test */ public function it_generates_routes_from_resource_with_form(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('register_user_with_form'); $this->assertNotNull($route); $this->assertEquals('/users/register', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.user:createAction', '_sylius' => [ 'form' => 'App\Form\Type\RegisterType', ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_form_options(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('register_user_with_form_options'); $this->assertNotNull($route); $this->assertEquals('/users/register', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.user:createAction', '_sylius' => [ 'form' => [ 'type' => 'App\Form\Type\RegisterType', 'options' => [ 'validation_groups' => ['sylius', 'my_custom_group'], ], ], ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_section(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_section'); $this->assertNotNull($route); $this->assertEquals('/books/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'section' => 'admin', ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_permission(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_permission'); $this->assertNotNull($route); $this->assertEquals('/books/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'permission' => true, ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_grid(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_grid'); $this->assertNotNull($route); $this->assertEquals('/books/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'grid' => 'book', ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_csrf_protection(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_csrf_protection'); $this->assertNotNull($route); $this->assertEquals('/books/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'csrf_protection' => false, ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_redirect(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('update_book_with_redirect'); $this->assertNotNull($route); $this->assertEquals('/books/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [ 'redirect' => 'referer', ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_redirect_options(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('update_book_with_redirect_options'); $this->assertNotNull($route); $this->assertEquals('/genre/{genreId}/books/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::updateAction', '_sylius' => [ 'redirect' => [ 'route' => 'app_genre_show', 'parameters' => ['id' => '$genreId'], ], ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_state_machine(): void { $this->markAsSkippedIfWinzouStateMachineIsNotAvailable(); self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('publish_book'); $this->assertNotNull($route); $this->assertEquals('/books/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::applyStateMachineTransitionAction', '_sylius' => [ 'state_machine' => [ 'graph' => 'app_book', 'transition' => 'publish', ], ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_custom_event_name(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('update_book_with_custom_event_name'); $this->assertNotNull($route); $this->assertEquals('/books/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::updateAction', '_sylius' => [ 'event' => 'customer_update', ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_return_content(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('update_book_with_return_content'); $this->assertNotNull($route); $this->assertEquals('/books/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::updateAction', '_sylius' => [ 'return_content' => true, ], ], $route->getDefaults()); } /** @test */ public function it_generates_routes_from_resource_with_legacy_attribute(): void { self::bootKernel(['environment' => 'test_with_attributes']); $container = self::getContainer(); $attributesLoader = $container->get('sylius.routing.loader.routes_attributes'); $routesCollection = $attributesLoader->__invoke(); $route = $routesCollection->get('show_book_with_legacy_attribute'); $this->assertNotNull($route); $this->assertEquals('/book/{id}', $route->getPath()); $this->assertEquals([ '_controller' => 'app.controller.book::showAction', '_sylius' => [], ], $route->getDefaults()); } private function markAsSkippedIfWinzouStateMachineIsNotAvailable(): void { if (!class_exists(winzouStateMachineBundle::class)) { $this->markTestSkipped('Winzou State machine is not available.'); } } } ================================================ FILE: tests/Bundle/Storage/CookieStorageTest.php ================================================ storage = new CookieStorage(); } public function testItImplementsStorageInterface(): void { $this->assertInstanceOf(StorageInterface::class, $this->storage); } public function testItDoesNotHaveValueWhenNotSetPreviously(): void { $this->assertFalse($this->storage->has('name')); $this->assertNull($this->storage->get('name')); } public function testItCanSetAndGetValue(): void { $this->storage->set('name', 'value'); $this->assertTrue($this->storage->has('name')); $this->assertSame('value', $this->storage->get('name')); } public function testItCanRemoveValue(): void { $this->storage->set('name', 'value'); $this->storage->remove('name'); $this->assertFalse($this->storage->has('name')); $this->assertNull($this->storage->get('name')); } public function testItReturnsDefaultValueWhenKeyNotFound(): void { $this->assertSame('default', $this->storage->get('name', 'default')); } public function testItReturnsAllStoredValues(): void { $this->storage->set('foo', 'bar'); $this->storage->set('buzz', 'lightyear'); $this->assertSame([ 'foo' => 'bar', 'buzz' => 'lightyear', ], $this->storage->all()); } } ================================================ FILE: tests/Bundle/Storage/SessionStorageTest.php ================================================ createMock(RequestStack::class); $requestStack ->method('getSession') ->willReturn(new Session(new MockArraySessionStorage())); $this->storage = new SessionStorage($requestStack); return; } $this->storage = new SessionStorage(new Session(new MockArraySessionStorage())); } public function testItImplementsStorageInterface(): void { $this->assertInstanceOf(StorageInterface::class, $this->storage); } /** * @dataProvider storageMethodsDataProvider */ public function testItThrowsStorageUnavailableExceptionWhenSessionNotAvailable(callable $method): void { if (!method_exists(RequestStack::class, 'getSession')) { $this->markTestSkipped('RequestStack::getSession() method does not exist in this Symfony version.'); } $requestStack = $this->createMock(RequestStack::class); $requestStack ->method('getSession') ->willThrowException(new SessionNotFoundException()); $storage = new SessionStorage($requestStack); $this->expectException(StorageUnavailableException::class); $method($storage); } /** * @return iterable */ public static function storageMethodsDataProvider(): iterable { yield 'has' => [fn (SessionStorage $storage) => $storage->has('name')]; yield 'get' => [fn (SessionStorage $storage) => $storage->get('name')]; yield 'set' => [fn (SessionStorage $storage) => $storage->set('name', 'value')]; yield 'remove' => [fn (SessionStorage $storage) => $storage->remove('name')]; yield 'all' => [fn (SessionStorage $storage) => $storage->all()]; } public function testItDoesNotHaveValueWhenNotSetPreviously(): void { $this->assertFalse($this->storage->has('name')); $this->assertNull($this->storage->get('name')); } public function testItCanSetAndGetValue(): void { $this->storage->set('name', 'value'); $this->assertTrue($this->storage->has('name')); $this->assertSame('value', $this->storage->get('name')); } public function testItCanRemoveValue(): void { $this->storage->set('name', 'value'); $this->storage->remove('name'); $this->assertFalse($this->storage->has('name')); $this->assertNull($this->storage->get('name')); } public function testItReturnsDefaultValueWhenKeyNotFound(): void { $this->assertSame('default', $this->storage->get('name', 'default')); } public function testItReturnsAllStoredValues(): void { $this->storage->set('foo', 'bar'); $this->storage->set('buzz', 'lightyear'); $this->assertSame([ 'foo' => 'bar', 'buzz' => 'lightyear', ], $this->storage->all()); } } ================================================ FILE: tests/Bundle/SyliusResourceBundleTest.php ================================================ getContainer(); $services = $container->getServiceIds(); $services = array_filter($services, function (string $serviceId): bool { return str_starts_with($serviceId, 'sylius.'); }); foreach ($services as $id) { Assert::assertNotNull($container->get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)); } } } ================================================ FILE: tests/Bundle/Twig/Context/LegacyContextFactoryTest.php ================================================ decorated = $this->createMock(ContextFactoryInterface::class); $this->factory = new LegacyContextFactory($this->decorated); } public function testItImplementsContextFactoryInterface(): void { $this->assertInstanceOf(ContextFactoryInterface::class, $this->factory); } public function testItAddsLegacyTwigVariablesWhenContextContainsOptions(): void { $data = new \stdClass(); $operation = $this->createMock(Operation::class); $requestConfiguration = $this->createMock(RequestConfiguration::class); $metadata = $this->createMock(MetadataInterface::class); $context = new Context( new RequestConfigurationOption($requestConfiguration), new MetadataOption($metadata), ); $this->decorated ->expects($this->once()) ->method('create') ->with($data, $operation, $context) ->willReturn(['resource' => $data]); $result = $this->factory->create($data, $operation, $context); $this->assertSame([ 'resource' => $data, 'configuration' => $requestConfiguration, 'metadata' => $metadata, ], $result); } public function testItDoesNotAddLegacyTwigVariablesWhenContextIsEmpty(): void { $data = new \stdClass(); $operation = $this->createMock(Operation::class); $context = new Context(); $this->decorated ->expects($this->once()) ->method('create') ->with($data, $operation, $context) ->willReturn(['resource' => $data]); $result = $this->factory->create($data, $operation, $context); $this->assertSame(['resource' => $data], $result); } } ================================================ FILE: tests/Bundle/Validator/DisabledValidatorTest.php ================================================ */ final class DisabledValidatorTest extends ConstraintValidatorTestCase { protected function createValidator(): DisabledValidator { return new DisabledValidator(); } public function testDoesNotApplyToNullValues(): void { $constraint = new Disabled(); $this->validator->validate(null, $constraint); $this->assertNoViolation(); } public function testThrowsExceptionIfSubjectDoesNotImplementToggleableInterface(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage(sprintf( '"%s" validates "%s" instances only', DisabledValidator::class, ToggleableInterface::class, )); $constraint = new Disabled(); $this->validator->validate(new \stdClass(), $constraint); } public function testAddsViolationWhenResourceIsEnabled(): void { $subject = $this->createMock(ToggleableInterface::class); $subject ->method('isEnabled') ->willReturn(true); $constraint = new Disabled(); $this->validator->validate($subject, $constraint); $this->buildViolation($constraint->message) ->assertRaised(); } public function testDoesNotAddViolationWhenResourceIsDisabled(): void { $subject = $this->createMock(ToggleableInterface::class); $subject ->method('isEnabled') ->willReturn(false); $constraint = new Disabled(); $this->validator->validate($subject, $constraint); $this->assertNoViolation(); } } ================================================ FILE: tests/Bundle/Validator/EnabledValidatorTest.php ================================================ */ final class EnabledValidatorTest extends ConstraintValidatorTestCase { protected function createValidator(): EnabledValidator { return new EnabledValidator(); } public function testDoesNotApplyToNullValues(): void { $constraint = new Enabled(); $this->validator->validate(null, $constraint); $this->assertNoViolation(); } public function testThrowsExceptionIfSubjectDoesNotImplementToggleableInterface(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage(sprintf( '"%s" validates "%s" instances only', EnabledValidator::class, ToggleableInterface::class, )); $constraint = new Enabled(); $this->validator->validate(new \stdClass(), $constraint); } public function testAddsViolationWhenResourceIsDisabled(): void { $subject = $this->createMock(ToggleableInterface::class); $subject ->method('isEnabled') ->willReturn(false); $constraint = new Enabled(); $this->validator->validate($subject, $constraint); $this->buildViolation($constraint->message) ->assertRaised(); } public function testDoesNotAddViolationWhenResourceIsEnabled(): void { $subject = $this->createMock(ToggleableInterface::class); $subject ->method('isEnabled') ->willReturn(true); $constraint = new Enabled(); $this->validator->validate($subject, $constraint); $this->assertNoViolation(); } } ================================================ FILE: tests/Bundle/Validator/UniqueWithinCollectionConstraintValidatorTest.php ================================================ */ final class UniqueWithinCollectionConstraintValidatorTest extends ConstraintValidatorTestCase { protected function createValidator(): UniqueWithinCollectionConstraintValidator { return new UniqueWithinCollectionConstraintValidator(); } public function testDoesNotAddViolationWhenCollectionIsEmpty(): void { $constraint = new UniqueWithinCollectionConstraint(); $this->validator->validate([], $constraint); $this->assertNoViolation(); } public function testDoesNotAddViolationWhenAllAttributesAreUnique(): void { $collection = [ $this->createEntityWithCode('CODE_1'), $this->createEntityWithCode('CODE_2'), $this->createEntityWithCode('CODE_3'), ]; $constraint = new UniqueWithinCollectionConstraint(); $this->validator->validate($collection, $constraint); $this->assertNoViolation(); } public function testDoesNotAddViolationWhenAttributeIsNull(): void { $collection = [ $this->createEntityWithCode(null), $this->createEntityWithCode(null), $this->createEntityWithCode('CODE_1'), ]; $constraint = new UniqueWithinCollectionConstraint(); $this->validator->validate($collection, $constraint); $this->assertNoViolation(); } public function testAddsViolationWhenTwoAttributesAreDuplicated(): void { $collection = [ $this->createEntityWithCode('CODE_1'), $this->createEntityWithCode('DUPLICATE'), $this->createEntityWithCode('DUPLICATE'), ]; $constraint = new UniqueWithinCollectionConstraint(); $this->setPropertyPath(''); $this->validator->validate($collection, $constraint); $this->buildViolation($constraint->message) ->atPath('[2].code') ->buildNextViolation($constraint->message) ->atPath('[1].code') ->assertRaised(); } public function testAddsViolationForFirstAndSecondOccurrenceOfDuplicate(): void { $collection = [ $this->createEntityWithCode('DUPLICATE'), $this->createEntityWithCode('DUPLICATE'), ]; $constraint = new UniqueWithinCollectionConstraint(); $this->setPropertyPath(''); $this->validator->validate($collection, $constraint); $this->buildViolation($constraint->message) ->atPath('[1].code') ->buildNextViolation($constraint->message) ->atPath('[0].code') ->assertRaised(); } public function testAddsViolationOnlyOnceForFirstOccurrenceWhenThreeOrMoreDuplicates(): void { $collection = [ $this->createEntityWithCode('DUPLICATE'), $this->createEntityWithCode('DUPLICATE'), $this->createEntityWithCode('DUPLICATE'), ]; $constraint = new UniqueWithinCollectionConstraint(); $this->setPropertyPath(''); $this->validator->validate($collection, $constraint); $this->buildViolation($constraint->message) ->atPath('[1].code') ->buildNextViolation($constraint->message) ->atPath('[0].code') ->buildNextViolation($constraint->message) ->atPath('[2].code') ->assertRaised(); } public function testHandlesMultipleDifferentDuplicates(): void { $collection = [ $this->createEntityWithCode('DUP_1'), $this->createEntityWithCode('DUP_1'), $this->createEntityWithCode('UNIQUE'), $this->createEntityWithCode('DUP_2'), $this->createEntityWithCode('DUP_2'), ]; $constraint = new UniqueWithinCollectionConstraint(); $this->setPropertyPath(''); $this->validator->validate($collection, $constraint); $this->buildViolation($constraint->message) ->atPath('[1].code') ->buildNextViolation($constraint->message) ->atPath('[0].code') ->buildNextViolation($constraint->message) ->atPath('[4].code') ->buildNextViolation($constraint->message) ->atPath('[3].code') ->assertRaised(); } public function testUsesCustomAttributePath(): void { $collection = [ $this->createEntityWithCustomAttribute('name', 'John'), $this->createEntityWithCustomAttribute('name', 'John'), ]; $constraint = new UniqueWithinCollectionConstraint(); $constraint->attributePath = 'name'; $this->setPropertyPath(''); $this->validator->validate($collection, $constraint); $this->buildViolation($constraint->message) ->atPath('[1].name') ->buildNextViolation($constraint->message) ->atPath('[0].name') ->assertRaised(); } public function testUsesCustomMessage(): void { $collection = [ $this->createEntityWithCode('DUPLICATE'), $this->createEntityWithCode('DUPLICATE'), ]; $constraint = new UniqueWithinCollectionConstraint(); $constraint->message = 'Custom error message'; $this->setPropertyPath(''); $this->validator->validate($collection, $constraint); $this->buildViolation('Custom error message') ->atPath('[1].code') ->buildNextViolation('Custom error message') ->atPath('[0].code') ->assertRaised(); } private function createEntityWithCode(?string $code): object { return new class($code) { public function __construct( private readonly ?string $code, ) { } public function getCode(): ?string { return $this->code; } }; } private function createEntityWithCustomAttribute(string $attributeName, mixed $value): object { return new class($attributeName, $value) { public function __construct( private readonly string $attributeName, private readonly mixed $value, ) { } public function __get(string $name): mixed { if ($name === $this->attributeName) { return $this->value; } throw new \Exception("Property {$name} does not exist"); } public function __isset(string $name): bool { return $name === $this->attributeName; } }; } }