protected function newAction()
    {
        $this->dispatch(EasyAdminEvents::PRE_NEW);

        $entity = $this->executeDynamicMethod('createNew<EntityName>Entity');

        $easyadmin = $this->request->attributes->get('easyadmin');
        $easyadmin['item'] = $entity;
        $this->request->attributes->set('easyadmin', $easyadmin);

        $fields = $this->entity['new']['fields'];

        $newForm = $this->executeDynamicMethod('create<EntityName>NewForm', [$entity, $fields]);

        $newForm->handleRequest($this->request);
        if ($newForm->isSubmitted() && $newForm->isValid()) {
            $this->dispatch(EasyAdminEvents::PRE_PERSIST, ['entity' => $entity]);
            $this->executeDynamicMethod('persist<EntityName>Entity', [$entity, $newForm]);
            $this->dispatch(EasyAdminEvents::POST_PERSIST, ['entity' => $entity]);

            return $this->redirectToReferrer();
        }

        $this->dispatch(EasyAdminEvents::POST_NEW, [
            'entity_fields' => $fields,
            'form' => $newForm,
            'entity' => $entity,
        ]);

        $parameters = [
            'form' => $newForm->createView(),
            'entity_fields' => $fields,
            'entity' => $entity,
        ];

        return $this->executeDynamicMethod('render<EntityName>Template', ['new', $this->entity['templates']['new'], $parameters]);
    }

protected function newAction ( ) { $ this -> dispatch ( EasyAdminEvents :: PRE_NEW ) ; $ entity = $ this -> executeDynamicMethod ( 'createNew<EntityName>Entity' ) ; $ easyadmin = $ this -> request -> attributes -> get ( 'easyadmin' ) ; $ easyadmin [ 'item' ] = $ entity ; $ this -> request -> attributes -> set ( 'easyadmin' , $ easyadmin ) ; $ fields = $ this -> entity [ 'new' ] [ 'fields' ] ; $ newForm = $ this -> executeDynamicMethod ( 'create<EntityName>NewForm' , [ $ entity , $ fields ] ) ; $ newForm -> handleRequest ( $ this -> request ) ; if ( $ newForm -> isSubmitted ( ) && $ newForm -> isValid ( ) ) { $ this -> dispatch ( EasyAdminEvents :: PRE_PERSIST , [ 'entity' => $ entity ] ) ; $ this -> executeDynamicMethod ( 'persist<EntityName>Entity' , [ $ entity , $ newForm ] ) ; $ this -> dispatch ( EasyAdminEvents :: POST_PERSIST , [ 'entity' => $ entity ] ) ; return $ this -> redirectToReferrer ( ) ; } $ this -> dispatch ( EasyAdminEvents :: POST_NEW , [ 'entity_fields' => $ fields , 'form' => $ newForm , 'entity' => $ entity , ] ) ; $ parameters = [ 'form' => $ newForm -> createView ( ) , 'entity_fields' => $ fields , 'entity' => $ entity , ] ; return $ this -> executeDynamicMethod ( 'render<EntityName>Template' , [ 'new' , $ this -> entity [ 'templates' ] [ 'new' ] , $ parameters ] ) ; }

The method that is executed when the user performs a 'new' action on an entity.

@return Response|RedirectResponse

The method that is executed when the user performs a new action on an entity .


protected function deleteAction()
    {
        $this->dispatch(EasyAdminEvents::PRE_DELETE);

        if ('DELETE' !== $this->request->getMethod()) {
            return $this->redirect($this->generateUrl('easyadmin', ['action' => 'list', 'entity' => $this->entity['name']]));
        }

        $id = $this->request->query->get('id');
        $form = $this->createDeleteForm($this->entity['name'], $id);
        $form->handleRequest($this->request);

        if ($form->isSubmitted() && $form->isValid()) {
            $easyadmin = $this->request->attributes->get('easyadmin');
            $entity = $easyadmin['item'];

            $this->dispatch(EasyAdminEvents::PRE_REMOVE, ['entity' => $entity]);

            try {
                $this->executeDynamicMethod('remove<EntityName>Entity', [$entity, $form]);
            } catch (ForeignKeyConstraintViolationException $e) {
                throw new EntityRemoveException(['entity_name' => $this->entity['name'], 'message' => $e->getMessage()]);
            }

            $this->dispatch(EasyAdminEvents::POST_REMOVE, ['entity' => $entity]);
        }

        $this->dispatch(EasyAdminEvents::POST_DELETE);

        return $this->redirectToReferrer();
    }

protected function deleteAction ( ) { $ this -> dispatch ( EasyAdminEvents :: PRE_DELETE ) ; if ( 'DELETE' !== $ this -> request -> getMethod ( ) ) { return $ this -> redirect ( $ this -> generateUrl ( 'easyadmin' , [ 'action' => 'list' , 'entity' => $ this -> entity [ 'name' ] ] ) ) ; } $ id = $ this -> request -> query -> get ( 'id' ) ; $ form = $ this -> createDeleteForm ( $ this -> entity [ 'name' ] , $ id ) ; $ form -> handleRequest ( $ this -> request ) ; if ( $ form -> isSubmitted ( ) && $ form -> isValid ( ) ) { $ easyadmin = $ this -> request -> attributes -> get ( 'easyadmin' ) ; $ entity = $ easyadmin [ 'item' ] ; $ this -> dispatch ( EasyAdminEvents :: PRE_REMOVE , [ 'entity' => $ entity ] ) ; try { $ this -> executeDynamicMethod ( 'remove<EntityName>Entity' , [ $ entity , $ form ] ) ; } catch ( ForeignKeyConstraintViolationException $ e ) { throw new EntityRemoveException ( [ 'entity_name' => $ this -> entity [ 'name' ] , 'message' => $ e -> getMessage ( ) ] ) ; } $ this -> dispatch ( EasyAdminEvents :: POST_REMOVE , [ 'entity' => $ entity ] ) ; } $ this -> dispatch ( EasyAdminEvents :: POST_DELETE ) ; return $ this -> redirectToReferrer ( ) ; }

The method that is executed when the user performs a 'delete' action to
remove any entity.

@return RedirectResponse

@throws EntityRemoveException

The method that is executed when the user performs a delete action to remove any entity .


protected function searchAction()
    {
        $this->dispatch(EasyAdminEvents::PRE_SEARCH);

        $query = \trim($this->request->query->get('query'));
        // if the search query is empty, redirect to the 'list' action
        if ('' === $query) {
            $queryParameters = \array_replace($this->request->query->all(), ['action' => 'list']);
            unset($queryParameters['query']);

            return $this->redirect($this->get('router')->generate('easyadmin', $queryParameters));
        }

        $searchableFields = $this->entity['search']['fields'];
        $defaultSortField = $this->entity['search']['sort']['field'] ?? null;
        $defaultSortDirection = isset($this->entity['search']['sort']['direction']) ? $this->entity['search']['sort']['direction'] : null;
        $paginator = $this->findBy(
            $this->entity['class'],
            $query,
            $searchableFields,
            $this->request->query->get('page', 1),
            $this->entity['list']['max_results'],
            $this->request->query->get('sortField', $defaultSortField),
            $this->request->query->get('sortDirection', $defaultSortDirection),
            $this->entity['search']['dql_filter']
        );
        $fields = $this->entity['list']['fields'];

        $this->dispatch(EasyAdminEvents::POST_SEARCH, [
            'fields' => $fields,
            'paginator' => $paginator,
        ]);

        $parameters = [
            'paginator' => $paginator,
            'fields' => $fields,
            'batch_form' => $this->createBatchForm($this->entity['name'])->createView(),
            'delete_form_template' => $this->createDeleteForm($this->entity['name'], '__id__')->createView(),
        ];

        return $this->executeDynamicMethod('render<EntityName>Template', ['search', $this->entity['templates']['list'], $parameters]);
    }

The method that is executed when the user performs a query on an entity.

@return Response

The method that is executed when the user performs a query on an entity .


protected function batchAction(): RedirectResponse
    {
        $batchForm = $this->createBatchForm($this->entity['name']);
        $batchForm->handleRequest($this->request);

        if ($batchForm->isSubmitted() && $batchForm->isValid()) {
            $actionName = $batchForm->get('name')->getData();
            $actionIds = $batchForm->get('ids')->getData();

            $this->executeDynamicMethod($actionName.'<EntityName>BatchAction', [$actionIds, $batchForm]);
        }

        return $this->redirectToReferrer();
    }

protected function batchAction ( ) : RedirectResponse { $ batchForm = $ this -> createBatchForm ( $ this -> entity [ 'name' ] ) ; $ batchForm -> handleRequest ( $ this -> request ) ; if ( $ batchForm -> isSubmitted ( ) && $ batchForm -> isValid ( ) ) { $ actionName = $ batchForm -> get ( 'name' ) -> getData ( ) ; $ actionIds = $ batchForm -> get ( 'ids' ) -> getData ( ) ; $ this -> executeDynamicMethod ( $ actionName . '<EntityName>BatchAction' , [ $ actionIds , $ batchForm ] ) ; } return $ this -> redirectToReferrer ( ) ; }

The method that is executed when the user performs a 'batch' action to any entity.

The method that is executed when the user performs a batch action to any entity .


protected function updateEntityProperty($entity, $property, $value)
    {
        $entityConfig = $this->entity;

        if (!$this->get('easyadmin.property_accessor')->isWritable($entity, $property)) {
            throw new \RuntimeException(\sprintf('The "%s" property of the "%s" entity is not writable.', $property, $entityConfig['name']));
        }

        $this->get('easyadmin.property_accessor')->setValue($entity, $property, $value);

        $this->dispatch(EasyAdminEvents::PRE_UPDATE, ['entity' => $entity, 'newValue' => $value]);
        $this->executeDynamicMethod('update<EntityName>Entity', [$entity]);
        $this->dispatch(EasyAdminEvents::POST_UPDATE, ['entity' => $entity, 'newValue' => $value]);

        $this->dispatch(EasyAdminEvents::POST_EDIT);
    }

It updates the value of some property of some entity to the new given value.

@param mixed  $entity   The instance of the entity to modify
@param string $property The name of the property to change
@param bool   $value    The new value of the property

@throws \RuntimeException

It updates the value of some property of some entity to the new given value .


protected function findAll($entityClass, $page = 1, $maxPerPage = 15, $sortField = null, $sortDirection = null, $dqlFilter = null)
    {
        if (null === $sortDirection || !\in_array(\strtoupper($sortDirection), ['ASC', 'DESC'])) {
            $sortDirection = 'DESC';
        }

        $queryBuilder = $this->executeDynamicMethod('create<EntityName>ListQueryBuilder', [$entityClass, $sortDirection, $sortField, $dqlFilter]);

        $this->dispatch(EasyAdminEvents::POST_LIST_QUERY_BUILDER, [
            'query_builder' => $queryBuilder,
            'sort_field' => $sortField,
            'sort_direction' => $sortDirection,
        ]);

        return $this->get('easyadmin.paginator')->createOrmPaginator($queryBuilder, $page, $maxPerPage);
    }

protected function findAll ( $ entityClass , $ page = 1 , $ maxPerPage = 15 , $ sortField = null , $ sortDirection = null , $ dqlFilter = null ) { if ( null === $ sortDirection || ! \ in_array ( \ strtoupper ( $ sortDirection ) , [ 'ASC' , 'DESC' ] ) ) { $ sortDirection = 'DESC' ; } $ queryBuilder = $ this -> executeDynamicMethod ( 'create<EntityName>ListQueryBuilder' , [ $ entityClass , $ sortDirection , $ sortField , $ dqlFilter ] ) ; $ this -> dispatch ( EasyAdminEvents :: POST_LIST_QUERY_BUILDER , [ 'query_builder' => $ queryBuilder , 'sort_field' => $ sortField , 'sort_direction' => $ sortDirection , ] ) ; return $ this -> get ( 'easyadmin.paginator' ) -> createOrmPaginator ( $ queryBuilder , $ page , $ maxPerPage ) ; }

Performs a database query to get all the records related to the given
entity. It supports pagination and field sorting.

@param string      $entityClass
@param int         $page
@param int         $maxPerPage
@param string|null $sortField
@param string|null $sortDirection
@param string|null $dqlFilter

@return Pagerfanta The paginated query results

Performs a database query to get all the records related to the given entity . It supports pagination and field sorting .


protected function createListQueryBuilder($entityClass, $sortDirection, $sortField = null, $dqlFilter = null)
    {
        return $this->get('easyadmin.query_builder')->createListQueryBuilder($this->entity, $sortField, $sortDirection, $dqlFilter);
    }

protected function createListQueryBuilder ( $ entityClass , $ sortDirection , $ sortField = null , $ dqlFilter = null ) { return $ this -> get ( 'easyadmin.query_builder' ) -> createListQueryBuilder ( $ this -> entity , $ sortField , $ sortDirection , $ dqlFilter ) ; }

Creates Query Builder instance for all the records.

@param string      $entityClass
@param string      $sortDirection
@param string|null $sortField
@param string|null $dqlFilter

@return QueryBuilder The Query Builder instance

Creates Query Builder instance for all the records .


protected function findBy($entityClass, $searchQuery, array $searchableFields, $page = 1, $maxPerPage = 15, $sortField = null, $sortDirection = null, $dqlFilter = null)
    {
        if (empty($sortDirection) || !\in_array(\strtoupper($sortDirection), ['ASC', 'DESC'])) {
            $sortDirection = 'DESC';
        }

        $queryBuilder = $this->executeDynamicMethod('create<EntityName>SearchQueryBuilder', [$entityClass, $searchQuery, $searchableFields, $sortField, $sortDirection, $dqlFilter]);

        $this->dispatch(EasyAdminEvents::POST_SEARCH_QUERY_BUILDER, [
            'query_builder' => $queryBuilder,
            'search_query' => $searchQuery,
            'searchable_fields' => $searchableFields,
        ]);

        return $this->get('easyadmin.paginator')->createOrmPaginator($queryBuilder, $page, $maxPerPage);
    }

protected function findBy ( $ entityClass , $ searchQuery , array $ searchableFields , $ page = 1 , $ maxPerPage = 15 , $ sortField = null , $ sortDirection = null , $ dqlFilter = null ) { if ( empty ( $ sortDirection ) || ! \ in_array ( \ strtoupper ( $ sortDirection ) , [ 'ASC' , 'DESC' ] ) ) { $ sortDirection = 'DESC' ; } $ queryBuilder = $ this -> executeDynamicMethod ( 'create<EntityName>SearchQueryBuilder' , [ $ entityClass , $ searchQuery , $ searchableFields , $ sortField , $ sortDirection , $ dqlFilter ] ) ; $ this -> dispatch ( EasyAdminEvents :: POST_SEARCH_QUERY_BUILDER , [ 'query_builder' => $ queryBuilder , 'search_query' => $ searchQuery , 'searchable_fields' => $ searchableFields , ] ) ; return $ this -> get ( 'easyadmin.paginator' ) -> createOrmPaginator ( $ queryBuilder , $ page , $ maxPerPage ) ; }

Performs a database query based on the search query provided by the user.
It supports pagination and field sorting.

@param string      $entityClass
@param string      $searchQuery
@param array       $searchableFields
@param int         $page
@param int         $maxPerPage
@param string|null $sortField
@param string|null $sortDirection
@param string|null $dqlFilter

@return Pagerfanta The paginated query results

Performs a database query based on the search query provided by the user . It supports pagination and field sorting .


protected function createSearchQueryBuilder($entityClass, $searchQuery, array $searchableFields, $sortField = null, $sortDirection = null, $dqlFilter = null)
    {
        return $this->get('easyadmin.query_builder')->createSearchQueryBuilder($this->entity, $searchQuery, $sortField, $sortDirection, $dqlFilter);
    }

protected function createSearchQueryBuilder ( $ entityClass , $ searchQuery , array $ searchableFields , $ sortField = null , $ sortDirection = null , $ dqlFilter = null ) { return $ this -> get ( 'easyadmin.query_builder' ) -> createSearchQueryBuilder ( $ this -> entity , $ searchQuery , $ sortField , $ sortDirection , $ dqlFilter ) ; }

Creates Query Builder instance for search query.

@param string      $entityClass
@param string      $searchQuery
@param array       $searchableFields
@param string|null $sortField
@param string|null $sortDirection
@param string|null $dqlFilter

@return QueryBuilder The Query Builder instance

Creates Query Builder instance for search query .


protected function createEntityFormBuilder($entity, $view)
    {
        $formOptions = $this->executeDynamicMethod('get<EntityName>EntityFormOptions', [$entity, $view]);

        return $this->get('form.factory')->createNamedBuilder(\mb_strtolower($this->entity['name']), EasyAdminFormType::class, $entity, $formOptions);
    }

protected function createEntityFormBuilder ( $ entity , $ view ) { $ formOptions = $ this -> executeDynamicMethod ( 'get<EntityName>EntityFormOptions' , [ $ entity , $ view ] ) ; return $ this -> get ( 'form.factory' ) -> createNamedBuilder ( \ mb_strtolower ( $ this -> entity [ 'name' ] ) , EasyAdminFormType :: class , $ entity , $ formOptions ) ; }

Creates the form builder of the form used to create or edit the given entity.

@param object $entity
@param string $view   The name of the view where this form is used ('new' or 'edit')

@return FormBuilder

Creates the form builder of the form used to create or edit the given entity .


protected function getEntityFormOptions($entity, $view)
    {
        $formOptions = $this->entity[$view]['form_options'];
        $formOptions['entity'] = $this->entity['name'];
        $formOptions['view'] = $view;

        return $formOptions;
    }

protected function getEntityFormOptions ( $ entity , $ view ) { $ formOptions = $ this -> entity [ $ view ] [ 'form_options' ] ; $ formOptions [ 'entity' ] = $ this -> entity [ 'name' ] ; $ formOptions [ 'view' ] = $ view ; return $ formOptions ; }

Retrieves the list of form options before sending them to the form builder.
This allows adding dynamic logic to the default form options.

@param object $entity
@param string $view

@return array

Retrieves the list of form options before sending them to the form builder . This allows adding dynamic logic to the default form options .


protected function createEntityForm($entity, array $entityProperties, $view)
    {
        if (\method_exists($this, $customMethodName = 'create'.$this->entity['name'].'EntityForm')) {
            $form = $this->{$customMethodName}($entity, $entityProperties, $view);
            if (!$form instanceof FormInterface) {
                throw new \UnexpectedValueException(\sprintf(
                    'The "%s" method must return a FormInterface, "%s" given.',
                    $customMethodName, \is_object($form) ? \get_class($form) : \gettype($form)
                ));
            }

            return $form;
        }

        $formBuilder = $this->executeDynamicMethod('create<EntityName>EntityFormBuilder', [$entity, $view]);

        if (!$formBuilder instanceof FormBuilderInterface) {
            throw new \UnexpectedValueException(\sprintf(
                'The "%s" method must return a FormBuilderInterface, "%s" given.',
                'createEntityForm', \is_object($formBuilder) ? \get_class($formBuilder) : \gettype($formBuilder)
            ));
        }

        return $formBuilder->getForm();
    }

Creates the form object used to create or edit the given entity.

@param object $entity
@param array  $entityProperties
@param string $view

@return FormInterface

@throws \Exception

Creates the form object used to create or edit the given entity .


protected function createDeleteForm($entityName, $entityId)
    {
        /** @var FormBuilder $formBuilder */
        $formBuilder = $this->get('form.factory')->createNamedBuilder('delete_form')
            ->setAction($this->generateUrl('easyadmin', ['action' => 'delete', 'entity' => $entityName, 'id' => $entityId]))
            ->setMethod('DELETE')
        ;
        $formBuilder->add('submit', SubmitType::class, ['label' => 'delete_modal.action', 'translation_domain' => 'EasyAdminBundle']);
        // needed to avoid submitting empty delete forms (see issue #1409)
        $formBuilder->add('_easyadmin_delete_flag', HiddenType::class, ['data' => '1']);

        return $formBuilder->getForm();
    }

Creates the form used to delete an entity. It must be a form because
the deletion of the entity are always performed with the 'DELETE' HTTP method,
which requires a form to work in the current browsers.

@param string     $entityName
@param int|string $entityId   When reusing the delete form for multiple entities, a pattern string is passed instead of an integer

@return Form|FormInterface

Creates the form used to delete an entity . It must be a form because the deletion of the entity are always performed with the DELETE HTTP method which requires a form to work in the current browsers .


protected function redirectToBackendHomepage()
    {
        $homepageConfig = $this->config['homepage'];

        $url = $homepageConfig['url'] ?? $this->get('router')->generate($homepageConfig['route'], $homepageConfig['params']);

        return $this->redirect($url);
    }

protected function redirectToBackendHomepage ( ) { $ homepageConfig = $ this -> config [ 'homepage' ] ; $ url = $ homepageConfig [ 'url' ] ?? $ this -> get ( 'router' ) -> generate ( $ homepageConfig [ 'route' ] , $ homepageConfig [ 'params' ] ) ; return $ this -> redirect ( $ url ) ; }

Generates the backend homepage and redirects to it.

Generates the backend homepage and redirects to it .


public function onKernelController(FilterControllerEvent $event)
    {
        $request = $event->getRequest();
        if ('easyadmin' !== $request->attributes->get('_route')) {
            return;
        }

        $currentController = $event->getController();
        // if the controller is defined in a class, $currentController is an array
        // otherwise do nothing because it's a Closure (rare but possible in Symfony)
        if (!\is_array($currentController)) {
            return;
        }

        // this condition happens when accessing the backend homepage, which
        // then redirects to the 'list' action of the first configured entity.
        if (null === $entityName = $request->query->get('entity')) {
            return;
        }

        $entity = $this->configManager->getEntityConfig($entityName);

        // if the entity doesn't define a custom controller, do nothing
        if (!isset($entity['controller'])) {
            return;
        }

        // build the full controller name using the 'class::method' syntax
        $controllerMethod = $currentController[1];
        $customController = $entity['controller'].'::'.$controllerMethod;

        $request->attributes->set('_controller', $customController);
        $newController = $this->resolver->getController($request);

        if (false === $newController) {
            throw new NotFoundHttpException(\sprintf('Unable to find the controller for path "%s". Check the "controller" configuration of the "%s" entity in your EasyAdmin backend.', $request->getPathInfo(), $entityName));
        }

        $event->setController($newController);
    }

Exchange default admin controller by custom entity admin controller.

@param FilterControllerEvent $event

@throws NotFoundHttpException

Exchange default admin controller by custom entity admin controller .


private function normalizeMenuConfig(array $menuConfig, array $backendConfig, $parentItemIndex = -1)
    {
        // if the backend doesn't define the menu configuration: create a default
        // menu configuration to display all its entities
        if (empty($menuConfig)) {
            foreach ($backendConfig['entities'] as $entityName => $entityConfig) {
                $menuConfig[] = ['entity' => $entityName, 'label' => $entityConfig['label']];
            }
        }

        // replaces the short config syntax:
        //   design.menu: ['Product', 'User']
        // by the expanded config syntax:
        //   design.menu: [{ entity: 'Product' }, { entity: 'User' }]
        foreach ($menuConfig as $i => $itemConfig) {
            if (\is_string($itemConfig)) {
                $itemConfig = ['entity' => $itemConfig];
            }

            $menuConfig[$i] = $itemConfig;
        }

        foreach ($menuConfig as $i => $itemConfig) {
            // normalize icon configuration
            if (!\array_key_exists('icon', $itemConfig)) {
                $itemConfig['icon'] = ($parentItemIndex > -1) ? '' : 'fa-folder-open';
            } elseif (empty($itemConfig['icon'])) {
                $itemConfig['icon'] = null;
            } else {
                $itemConfig['icon'] = 'fa-'.$itemConfig['icon'];
            }

            // normalize css_class configuration
            if (!\array_key_exists('css_class', $itemConfig)) {
                $itemConfig['css_class'] = '';
            }

            // normalize submenu configuration (only for main menu items)
            if (-1 === $parentItemIndex && !isset($itemConfig['children'])) {
                $itemConfig['children'] = [];
            }

            // normalize 'default' option, which sets the menu item used as the backend index
            if (!\array_key_exists('default', $itemConfig)) {
                $itemConfig['default'] = false;
            } else {
                $itemConfig['default'] = (bool) $itemConfig['default'];
            }

            // normalize 'target' option, which allows to open menu items in different windows or tabs
            if (!\array_key_exists('target', $itemConfig)) {
                $itemConfig['target'] = false;
            } else {
                $itemConfig['target'] = (string) $itemConfig['target'];
            }

            // normalize 'rel' option, which adds html5 rel attribute (https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types)
            if (!\array_key_exists('rel', $itemConfig)) {
                $itemConfig['rel'] = \array_key_exists('url', $itemConfig) ? 'noreferrer' : false;
            } else {
                $itemConfig['rel'] = (string) $itemConfig['rel'];
            }

            $menuConfig[$i] = $itemConfig;
        }

        return $menuConfig;
    }

Normalizes the different shortcut notations of the menu config to simplify
further processing.

@param array $menuConfig
@param array $backendConfig
@param int   $parentItemIndex The index of the parent item for this menu item (allows to treat submenus differently)

@return array

Normalizes the different shortcut notations of the menu config to simplify further processing .


public function getEntityConfiguration($entityName)
    {
        return null !== $this->getBackendConfiguration('entities.'.$entityName)
            ? $this->configManager->getEntityConfig($entityName)
            : null;
    }

public function getEntityConfiguration ( $ entityName ) { return null !== $ this -> getBackendConfiguration ( 'entities.' . $ entityName ) ? $ this -> configManager -> getEntityConfig ( $ entityName ) : null ; }

Returns the entire configuration of the given entity.

@param string $entityName

@return array|null

Returns the entire configuration of the given entity .


public function isActionEnabled($view, $action, $entityName)
    {
        return $this->configManager->isActionEnabled($entityName, $view, $action);
    }

public function isActionEnabled ( $ view , $ action , $ entityName ) { return $ this -> configManager -> isActionEnabled ( $ entityName , $ view , $ action ) ; }

Checks whether the given 'action' is enabled for the given 'entity'.

@param string $view
@param string $action
@param string $entityName

@return bool

Checks whether the given action is enabled for the given entity .


public function getActionConfiguration($view, $action, $entityName)
    {
        return $this->configManager->getActionConfig($entityName, $view, $action);
    }

public function getActionConfiguration ( $ view , $ action , $ entityName ) { return $ this -> configManager -> getActionConfig ( $ entityName , $ view , $ action ) ; }

Returns the full action configuration for the given 'entity' and 'view'.

@param string $view
@param string $action
@param string $entityName

@return array

Returns the full action configuration for the given entity and view .


public function transchoice($message, $count, array $arguments = [], $domain = null, $locale = null)
    {
        if (null === $this->translator) {
            return strtr($message, $arguments);
        }

        return $this->translator->trans($message, array_merge(['%count%' => $count], $arguments), $domain, $locale);
    }

public function transchoice ( $ message , $ count , array $ arguments = [ ] , $ domain = null , $ locale = null ) { if ( null === $ this -> translator ) { return strtr ( $ message , $ arguments ) ; } return $ this -> translator -> trans ( $ message , array_merge ( [ '%count%' => $ count ] , $ arguments ) , $ domain , $ locale ) ; }

Remove this filter when the Symfony's requirement is equal or greater than 4.2
and use the built-in trans filter instead with a %count% parameter.

Remove this filter when the Symfony s requirement is equal or greater than 4 . 2 and use the built - in trans filter instead with a %count% parameter .


public function createOrmPaginator($queryBuilder, $page = 1, $maxPerPage = self::MAX_ITEMS)
    {
        $query = $queryBuilder->getQuery();
        if (0 === \count($queryBuilder->getDQLPart('join'))) {
            $query->setHint(CountWalker::HINT_DISTINCT, false);
        }

        // don't change the following line (you did that twice in the past and broke everything)
        $paginator = new Pagerfanta(new DoctrineORMAdapter($query, true, false));
        $paginator->setMaxPerPage($maxPerPage);
        $paginator->setCurrentPage($page);

        return $paginator;
    }

Creates a Doctrine ORM paginator for the given query builder.

@param DoctrineQuery|DoctrineQueryBuilder $queryBuilder
@param int                                $page
@param int                                $maxPerPage

@return Pagerfanta

Creates a Doctrine ORM paginator for the given query builder .


private function doProcessConfig($backendConfig): array
    {
        foreach ($this->configPasses as $configPass) {
            $backendConfig = $configPass->process($backendConfig);
        }

        return $backendConfig;
    }

private function doProcessConfig ( $ backendConfig ) : array { foreach ( $ this -> configPasses as $ configPass ) { $ backendConfig = $ configPass -> process ( $ backendConfig ) ; } return $ backendConfig ; }

It processes the given backend configuration to generate the fully
processed configuration used in the application.

@param array $backendConfig

@return array

It processes the given backend configuration to generate the fully processed configuration used in the application .


public function initializeRequest(GenericEvent $event)
    {
        $request = null;
        if (null !== $this->requestStack) {
            $request = $this->requestStack->getCurrentRequest();
        }

        if (null === $request) {
            return;
        }

        $request->attributes->set('easyadmin', [
            'entity' => $entity = $event->getArgument('entity'),
            'view' => $request->query->get('action', 'list'),
            'item' => ($id = $request->query->get('id')) ? $this->findCurrentItem($entity, $id) : null,
        ]);
    }

public function initializeRequest ( GenericEvent $ event ) { $ request = null ; if ( null !== $ this -> requestStack ) { $ request = $ this -> requestStack -> getCurrentRequest ( ) ; } if ( null === $ request ) { return ; } $ request -> attributes -> set ( 'easyadmin' , [ 'entity' => $ entity = $ event -> getArgument ( 'entity' ) , 'view' => $ request -> query -> get ( 'action' , 'list' ) , 'item' => ( $ id = $ request -> query -> get ( 'id' ) ) ? $ this -> findCurrentItem ( $ entity , $ id ) : null , ] ) ; }

Adds to the request some attributes with useful information, such as the
current entity and the selected item, if any.

@param GenericEvent $event

Adds to the request some attributes with useful information such as the current entity and the selected item if any .


private function findCurrentItem(array $entityConfig, $itemId)
    {
        if (null === $manager = $this->doctrine->getManagerForClass($entityConfig['class'])) {
            throw new \RuntimeException(\sprintf('There is no Doctrine Entity Manager defined for the "%s" class', $entityConfig['class']));
        }

        if (null === $entity = $manager->getRepository($entityConfig['class'])->find($itemId)) {
            throw new EntityNotFoundException(['entity_name' => $entityConfig['name'], 'entity_id_name' => $entityConfig['primary_key_field_name'], 'entity_id_value' => $itemId]);
        }

        return $entity;
    }

Looks for the object that corresponds to the selected 'id' of the current entity.

@param array $entityConfig
@param mixed $itemId

@return object The entity

@throws EntityNotFoundException
@throws \RuntimeException

Looks for the object that corresponds to the selected id of the current entity .


private function getAttributesNormalizer()
    {
        return function (Options $options, $value) {
            return \array_replace([
                'id' => \sprintf('%s-%s-form', $options['view'], \mb_strtolower($options['entity'])),
            ], $value);
        };
    }

private function getAttributesNormalizer ( ) { return function ( Options $ options , $ value ) { return \ array_replace ( [ 'id' => \ sprintf ( '%s-%s-form' , $ options [ 'view' ] , \ mb_strtolower ( $ options [ 'entity' ] ) ) , ] , $ value ) ; } ; }

Returns a closure normalizing the form html attributes.

@return \Closure

Returns a closure normalizing the form html attributes .


private function processEntityPropertiesMetadata(ClassMetadata $entityMetadata)
    {
        $entityPropertiesMetadata = [];

        if ($entityMetadata->isIdentifierComposite) {
            throw new \RuntimeException(\sprintf("The '%s' entity isn't valid because it contains a composite primary key.", $entityMetadata->name));
        }

        // introspect regular entity fields
        foreach ($entityMetadata->fieldMappings as $fieldName => $fieldMetadata) {
            $entityPropertiesMetadata[$fieldName] = $fieldMetadata;
        }

        // introspect fields for entity associations
        foreach ($entityMetadata->associationMappings as $fieldName => $associationMetadata) {
            $entityPropertiesMetadata[$fieldName] = \array_merge($associationMetadata, [
                'type' => 'association',
                'associationType' => $associationMetadata['type'],
            ]);

            // associations different from *-to-one cannot be sorted
            if ($associationMetadata['type'] & ClassMetadata::TO_MANY) {
                $entityPropertiesMetadata[$fieldName]['sortable'] = false;
            }
        }

        return $entityPropertiesMetadata;
    }

Takes the entity metadata introspected via Doctrine and completes its
contents to simplify data processing for the rest of the application.

@param ClassMetadata $entityMetadata The entity metadata introspected via Doctrine

@return array The entity properties metadata provided by Doctrine

@throws \RuntimeException

Takes the entity metadata introspected via Doctrine and completes its contents to simplify data processing for the rest of the application .


private function getDefaultActions($view)
    {
        $defaultActions = [];
        $defaultActionsConfig = $this->getDefaultActionsConfig($view);

        // actions are displayed in the same order as defined in this array
        $actionsEnabledByView = [
            'edit' => ['delete', 'list'],
            'list' => ['edit', 'delete', 'new', 'search'],
            'new' => ['list'],
            'show' => ['edit', 'delete', 'list'],
        ];

        foreach ($actionsEnabledByView[$view] as $actionName) {
            $defaultActions[$actionName] = $defaultActionsConfig[$actionName];
        }

        return $defaultActions;
    }

Returns the built-in actions defined by EasyAdmin for the given view.
This allows to provide some nice defaults for backends that don't
define their own actions.

@param string $view

@return array

Returns the built - in actions defined by EasyAdmin for the given view . This allows to provide some nice defaults for backends that don t define their own actions .


public function handleViolations(FormEvent $event)
    {
        $formTabs = $event->getForm()->getConfig()->getAttribute('easyadmin_form_tabs');

        $firstTabWithErrors = null;
        foreach ($event->getForm() as $child) {
            $errors = $child->getErrors(true);

            if (\count($errors) > 0) {
                $formTab = $child->getConfig()->getAttribute('easyadmin_form_tab');
                $formTabs[$formTab]['errors'] += \count($errors);

                if (null === $firstTabWithErrors) {
                    $firstTabWithErrors = $formTab;
                }
            }
        }

        // ensure that the first tab with errors is displayed
        $firstTab = \key($formTabs);
        if ($firstTab !== $firstTabWithErrors) {
            $formTabs[$firstTab]['active'] = false;
            $formTabs[$firstTabWithErrors]['active'] = true;
        }
    }

Deal with form constraint violations. This method has to be executed with
a negative priority to make sure that the validation process is done.

@param FormEvent $event

Deal with form constraint violations . This method has to be executed with a negative priority to make sure that the validation process is done .


public function find($entity, $query, $page = 1)
    {
        if (empty($entity) || empty($query)) {
            return ['results' => []];
        }

        $backendConfig = $this->configManager->getBackendConfig();
        if (!isset($backendConfig['entities'][$entity])) {
            throw new \InvalidArgumentException(\sprintf('The "entity" argument must contain the name of an entity managed by EasyAdmin ("%s" given).', $entity));
        }

        $paginator = $this->finder->findByAllProperties($backendConfig['entities'][$entity], $query, $page, $backendConfig['show']['max_results']);

        return [
            'results' => $this->processResults($paginator->getCurrentPageResults(), $backendConfig['entities'][$entity]),
            'has_next_page' => $paginator->hasNextPage(),
        ];
    }

Finds the values of the given entity which match the query provided.

@param string $entity
@param string $query
@param int    $page

@return array

@throws \InvalidArgumentException

Finds the values of the given entity which match the query provided .


private function processConfigFiles(array $configs)
    {
        $existingEntityNames = [];

        foreach ($configs as $i => $config) {
            if (\array_key_exists('entities', $config)) {
                $processedConfig = [];

                foreach ($config['entities'] as $key => $value) {
                    $entityConfig = $this->normalizeEntityConfig($key, $value);
                    $entityName = $this->getUniqueEntityName($key, $entityConfig, $existingEntityNames);
                    $entityConfig['name'] = $entityName;

                    $processedConfig[$entityName] = $entityConfig;

                    $existingEntityNames[] = $entityName;
                }

                $config['entities'] = $processedConfig;
            }

            $configs[$i] = $config;
        }

        return $configs;
    }

private function processConfigFiles ( array $ configs ) { $ existingEntityNames = [ ] ; foreach ( $ configs as $ i => $ config ) { if ( \ array_key_exists ( 'entities' , $ config ) ) { $ processedConfig = [ ] ; foreach ( $ config [ 'entities' ] as $ key => $ value ) { $ entityConfig = $ this -> normalizeEntityConfig ( $ key , $ value ) ; $ entityName = $ this -> getUniqueEntityName ( $ key , $ entityConfig , $ existingEntityNames ) ; $ entityConfig [ 'name' ] = $ entityName ; $ processedConfig [ $ entityName ] = $ entityConfig ; $ existingEntityNames [ ] = $ entityName ; } $ config [ 'entities' ] = $ processedConfig ; } $ configs [ $ i ] = $ config ; } return $ configs ; }

This method allows to define the entity configuration is several files.
Without this, Symfony doesn't merge correctly the 'entities' config key
defined in different files.

@param array $configs

@return array

This method allows to define the entity configuration is several files . Without this Symfony doesn t merge correctly the entities config key defined in different files .


private function normalizeEntityConfig($entityName, $entityConfig)
    {
        // normalize config formats #1 and #2 to use the 'class' option as config format #3
        if (!\is_array($entityConfig)) {
            $entityConfig = ['class' => $entityConfig];
        }

        // if config format #3 is used, ensure that it defines the 'class' option
        if (!isset($entityConfig['class'])) {
            throw new \RuntimeException(\sprintf('The "%s" entity must define its associated Doctrine entity class using the "class" option.', $entityName));
        }

        return $entityConfig;
    }

Transforms the two simple configuration formats into the full expanded
configuration. This allows to reuse the same method to process any of the
different configuration formats.

These are the two simple formats allowed:

# Config format #1: no custom entity name
easy_admin:
entities:
- AppBundle\Entity\User

# Config format #2: simple config with custom entity name
easy_admin:
entities:
User: AppBundle\Entity\User

And this is the full expanded configuration syntax generated by this method:

# Config format #3: expanded entity configuration with 'class' parameter
easy_admin:
entities:
User:
class: AppBundle\Entity\User

@param mixed $entityName
@param mixed $entityConfig

@return array

@throws \RuntimeException

Transforms the two simple configuration formats into the full expanded configuration . This allows to reuse the same method to process any of the different configuration formats .


private function getUniqueEntityName($entityName, array $entityConfig, array $existingEntityNames)
    {
        // the shortcut config syntax doesn't require to give entities a name
        if (\is_numeric($entityName)) {
            $entityClassParts = \explode('\\', $entityConfig['class']);
            $entityName = \end($entityClassParts);
        }

        $i = 2;
        $uniqueName = $entityName;
        while (\in_array($uniqueName, $existingEntityNames)) {
            $uniqueName = $entityName.($i++);
        }

        $entityName = $uniqueName;

        // make sure that the entity name is valid as a PHP method name
        // (this is required to allow extending the backend with a custom controller)
        if (!$this->isValidMethodName($entityName)) {
            throw new \InvalidArgumentException(\sprintf('The name of the "%s" entity contains invalid characters (allowed: letters, numbers, underscores; the first character cannot be a number).', $entityName));
        }

        return $entityName;
    }

The name of the entity is included in the URLs of the backend to define
the entity used to perform the operations. Obviously, the entity name
must be unique to identify entities unequivocally.

This method ensures that the given entity name is unique among all the
previously existing entities passed as the second argument. This is
achieved by iteratively appending a suffix until the entity name is
guaranteed to be unique.

@param string $entityName
@param array  $entityConfig
@param array  $existingEntityNames

@return string The entity name transformed to be unique

The name of the entity is included in the URLs of the backend to define the entity used to perform the operations . Obviously the entity name must be unique to identify entities unequivocally .


private function normalizeViewConfig(array $backendConfig)
    {
        foreach ($backendConfig['entities'] as $entityName => $entityConfig) {
            // if the original 'search' config doesn't define its own DQL filter, use the one form 'list'
            if (!isset($entityConfig['search']) || !\array_key_exists('dql_filter', $entityConfig['search'])) {
                $entityConfig['search']['dql_filter'] = $entityConfig['list']['dql_filter'] ?? null;
            }

            foreach (['edit', 'form', 'list', 'new', 'search', 'show'] as $view) {
                $entityConfig[$view] = \array_replace_recursive(
                    $this->defaultViewConfig[$view],
                    $entityConfig[$view] ?? []
                );
            }

            $backendConfig['entities'][$entityName] = $entityConfig;
        }

        return $backendConfig;
    }

Normalizes the view configuration when some of them doesn't define any
configuration.

@param array $backendConfig

@return array

Normalizes the view configuration when some of them doesn t define any configuration .


private function mergeFormConfig(array $parentConfig, array $childConfig)
    {
        // save the fields config for later processing
        $parentFields = $parentConfig['fields'] ?? [];
        $childFields = $childConfig['fields'] ?? [];
        $removedFieldNames = $this->getRemovedFieldNames($childFields);

        // first, perform a recursive replace to merge both configs
        $mergedConfig = \array_replace_recursive($parentConfig, $childConfig);

        // merge the config of each field individually
        $mergedFields = [];
        foreach ($parentFields as $parentFieldName => $parentFieldConfig) {
            if (isset($parentFieldConfig['property']) && \in_array($parentFieldConfig['property'], $removedFieldNames)) {
                continue;
            }

            if (!isset($parentFieldConfig['property'])) {
                // this isn't a regular form field but a special design element (group, section, divider); add it
                $mergedFields[$parentFieldName] = $parentFieldConfig;
                continue;
            }

            $childFieldConfig = $this->findFieldConfigByProperty($childFields, $parentFieldConfig['property']) ?: [];
            $mergedFields[$parentFieldName] = \array_replace_recursive($parentFieldConfig, $childFieldConfig);
        }

        // add back the fields that are defined in child config but not in parent config
        foreach ($childFields as $childFieldName => $childFieldConfig) {
            $isFormDesignElement = !isset($childFieldConfig['property']);
            $isNotRemovedField = isset($childFieldConfig['property']) && 0 !== \strpos($childFieldConfig['property'], '-');
            $isNotAlreadyIncluded = isset($childFieldConfig['property']) && !\array_key_exists($childFieldConfig['property'], $mergedFields);

            if ($isFormDesignElement || ($isNotRemovedField && $isNotAlreadyIncluded)) {
                $mergedFields[$childFieldName] = $childFieldConfig;
            }
        }

        // finally, copy the processed field config into the merged config
        $mergedConfig['fields'] = $mergedFields;

        return $mergedConfig;
    }

Merges the form configuration recursively from the 'form' view to the
'edit' and 'new' views. It processes the configuration of the form fields
in a special way to keep all their configuration and allow overriding and
removing of fields.

@param array $parentConfig The config of the 'form' view
@param array $childConfig  The config of the 'edit' and 'new' views

@return array

Merges the form configuration recursively from the form view to the edit and new views . It processes the configuration of the form fields in a special way to keep all their configuration and allow overriding and removing of fields .


public function createListQueryBuilder(array $entityConfig, $sortField = null, $sortDirection = null, $dqlFilter = null)
    {
        /* @var EntityManager $em */
        $em = $this->doctrine->getManagerForClass($entityConfig['class']);
        /* @var ClassMetadata $classMetadata */
        $classMetadata = $em->getClassMetadata($entityConfig['class']);
        /* @var DoctrineQueryBuilder $queryBuilder */
        $queryBuilder = $em->createQueryBuilder()
            ->select('entity')
            ->from($entityConfig['class'], 'entity')
        ;

        $isSortedByDoctrineAssociation = $this->isDoctrineAssociation($classMetadata, $sortField);
        if ($isSortedByDoctrineAssociation) {
            $sortFieldParts = \explode('.', $sortField);
            $queryBuilder->leftJoin('entity.'.$sortFieldParts[0], $sortFieldParts[0]);
        }

        if (!empty($dqlFilter)) {
            $queryBuilder->andWhere($dqlFilter);
        }

        if (null !== $sortField) {
            $queryBuilder->orderBy(\sprintf('%s%s', $isSortedByDoctrineAssociation ? '' : 'entity.', $sortField), $sortDirection);
        }

        return $queryBuilder;
    }

Creates the query builder used to get all the records displayed by the
"list" view.

@param array       $entityConfig
@param string|null $sortField
@param string|null $sortDirection
@param string|null $dqlFilter

@return DoctrineQueryBuilder

Creates the query builder used to get all the records displayed by the list view .


protected function isDoctrineAssociation(ClassMetadata $classMetadata, $fieldName)
    {
        if (null === $fieldName) {
            return false;
        }

        $fieldNameParts = \explode('.', $fieldName);

        return false !== \strpos($fieldName, '.') && !\array_key_exists($fieldNameParts[0], $classMetadata->embeddedClasses);
    }

protected function isDoctrineAssociation ( ClassMetadata $ classMetadata , $ fieldName ) { if ( null === $ fieldName ) { return false ; } $ fieldNameParts = \ explode ( '.' , $ fieldName ) ; return false !== \ strpos ( $ fieldName , '.' ) && ! \ array_key_exists ( $ fieldNameParts [ 0 ] , $ classMetadata -> embeddedClasses ) ; }

Checking if the field name contains a '.' is not enough to decide if it's a
Doctrine association. This also happens when using embedded classes, so the
embeddedClasses property from Doctrine class metadata must be checked too.

@param ClassMetadata $classMetadata
@param string|null   $fieldName

@return bool

Checking if the field name contains a . is not enough to decide if it s a Doctrine association . This also happens when using embedded classes so the embeddedClasses property from Doctrine class metadata must be checked too .


private function processFieldConfig(array $backendConfig)
    {
        foreach ($backendConfig['entities'] as $entityName => $entityConfig) {
            foreach (['edit', 'list', 'new', 'search', 'show'] as $view) {
                foreach ($entityConfig[$view]['fields'] as $fieldName => $fieldConfig) {
                    if (!isset($fieldConfig['label']) && 'id' === $fieldConfig['property']) {
                        // if the field is called 'id' and doesn't define a custom label, use 'ID' as label to
                        // improve the readability of the label, which is usually related to a primary key
                        $fieldConfig['label'] = 'ID';
                    } elseif (isset($fieldConfig['label']) && false === $fieldConfig['label']) {
                        // if the label is the special value 'false', label must be hidden (use an empty string as the label)
                        $fieldConfig['label'] = '';
                        $fieldConfig['sortable'] = false;
                    } elseif (null === $fieldConfig['label'] && 0 !== strpos($fieldConfig['property'], '_easyadmin_form_design_element_')) {
                        // else, generate the label automatically from its name (except if it's a
                        // special element created to render complex forms)
                        $fieldConfig['label'] = $this->humanize($fieldConfig['property']);
                    }

                    $backendConfig['entities'][$entityName][$view]['fields'][$fieldName] = $fieldConfig;
                }
            }
        }

        return $backendConfig;
    }

This methods makes some minor tweaks in fields configuration to improve
the user experience.

@param array $backendConfig

@return array

This methods makes some minor tweaks in fields configuration to improve the user experience .


private function getExcludedFieldNames($view, array $entityConfig)
    {
        $excludedFieldNames = [
            'edit' => [$entityConfig['primary_key_field_name']],
            'list' => ['password', 'salt', 'slug', 'updatedAt', 'uuid'],
            'new' => [$entityConfig['primary_key_field_name']],
            'search' => ['password', 'salt'],
            'show' => [],
        ];

        return isset($excludedFieldNames[$view]) ? $excludedFieldNames[$view] : [];
    }

private function getExcludedFieldNames ( $ view , array $ entityConfig ) { $ excludedFieldNames = [ 'edit' => [ $ entityConfig [ 'primary_key_field_name' ] ] , 'list' => [ 'password' , 'salt' , 'slug' , 'updatedAt' , 'uuid' ] , 'new' => [ $ entityConfig [ 'primary_key_field_name' ] ] , 'search' => [ 'password' , 'salt' ] , 'show' => [ ] , ] ; return isset ( $ excludedFieldNames [ $ view ] ) ? $ excludedFieldNames [ $ view ] : [ ] ; }

Returns the list of excluded field names for the given view.

@param string $view
@param array  $entityConfig

@return array

Returns the list of excluded field names for the given view .


private function filterFieldList(array $fields, array $excludedFieldNames, array $excludedFieldTypes, $maxNumFields)
    {
        $filteredFields = [];

        foreach ($fields as $name => $metadata) {
            if (!\in_array($name, $excludedFieldNames) && !\in_array($metadata['type'], $excludedFieldTypes)) {
                $filteredFields[$name] = $fields[$name];
            }
        }

        if (\count($filteredFields) > $maxNumFields) {
            $filteredFields = \array_slice($filteredFields, 0, $maxNumFields, true);
        }

        return $filteredFields;
    }

private function filterFieldList ( array $ fields , array $ excludedFieldNames , array $ excludedFieldTypes , $ maxNumFields ) { $ filteredFields = [ ] ; foreach ( $ fields as $ name => $ metadata ) { if ( ! \ in_array ( $ name , $ excludedFieldNames ) && ! \ in_array ( $ metadata [ 'type' ] , $ excludedFieldTypes ) ) { $ filteredFields [ $ name ] = $ fields [ $ name ] ; } } if ( \ count ( $ filteredFields ) > $ maxNumFields ) { $ filteredFields = \ array_slice ( $ filteredFields , 0 , $ maxNumFields , true ) ; } return $ filteredFields ; }

Filters a list of fields excluding the given list of field names and field types.

@param array    $fields
@param string[] $excludedFieldNames
@param string[] $excludedFieldTypes
@param int      $maxNumFields

@return array The filtered list of fields

Filters a list of fields excluding the given list of field names and field types .


public static function run(string $command, string $cwd = null): array
    {
        $descriptors = [
            0 => ['pipe', 'r'], // stdin - read channel
            1 => ['pipe', 'w'], // stdout - write channel
            2 => ['pipe', 'w'], // stdout - error channel
            3 => ['pipe', 'r'], // stdin - This is the pipe we can feed the password into
        ];

        $process = proc_open($command, $descriptors, $pipes, $cwd);

        if (!\is_resource($process)) {
            throw new \RuntimeException('Can\'t open resource with proc_open.');
        }

        // Nothing to push to input.
        fclose($pipes[0]);

        $output = stream_get_contents($pipes[1]);
        fclose($pipes[1]);

        $error = stream_get_contents($pipes[2]);
        fclose($pipes[2]);

        fclose($pipes[3]);

        // Close all pipes before proc_close! $code === 0 is success.
        $code = proc_close($process);

        return [trim($code), trim($output), trim($error)];
    }

run a command. it is support windows
@param string $command
@param string|null $cwd
@return array
@throws \RuntimeException


public static function deleteOrdCheck(string $stmt, int $offset)
    {
        $sqlType = self::OTHER;
        switch ($stmt[$offset + 1]) {
            case 'E':
            case 'e':
                $sqlType = self::dCheck($stmt, $offset);
                break;
            case 'R':
            case 'r':
                $sqlType = self::dropCheck($stmt, $offset);
                break;
            default:
                $sqlType = self::OTHER;
        }
        return $sqlType;
    }

public static function deleteOrdCheck ( string $ stmt , int $ offset ) { $ sqlType = self :: OTHER ; switch ( $ stmt [ $ offset + 1 ] ) { case 'E' : case 'e' : $ sqlType = self :: dCheck ( $ stmt , $ offset ) ; break ; case 'R' : case 'r' : $ sqlType = self :: dropCheck ( $ stmt , $ offset ) ; break ; default : $ sqlType = self :: OTHER ; } return $ sqlType ; }


public static function dCheck(string $stmt, int $offset)
    {
        if (strlen($stmt) > $offset + 4) {
            $res = self::describeCheck($stmt, $offset);
            if ($res == self::DESCRIBE) {
                return $res;
            }
        }
        // continue check
        if (strlen($stmt) > $offset + 6) {
            $c1 = $stmt[++$offset];
            $c2 = $stmt[++$offset];
            $c3 = $stmt[++$offset];
            $c4 = $stmt[++$offset];
            $c5 = $stmt[++$offset];
            $c6 = $stmt[++$offset];
            if (($c1 == 'E' || $c1 == 'e') && ($c2 == 'L' || $c2 == 'l')
                && ($c3 == 'E' || $c3 == 'e') && ($c4 == 'T' || $c4 == 't')
                && ($c5 == 'E' || $c5 == 'e')
                && ($c6 == ' ' || $c6 == '\t' || $c6 == '\r' || $c6 == '\n')) {
                return self::DELETE;
            }
        }
        return self::OTHER;
    }

public
static
function
dCheck
(
string
$
stmt
,
int
$
offset
)
{
if
(
strlen
(
$
stmt
)
>
$
offset
+
4
)
{
$
res
=
self
::
describeCheck
(
$
stmt
,
$
offset
)
;
if
(
$
res
==
self
::
DESCRIBE
)
{
return
$
res
;
}
}
// continue check
if
(
strlen
(
$
stmt
)
>
$
offset
+
6
)
{
$
c1
=
$
stmt
[
++
$
offset
]
;
$
c2
=
$
stmt
[
++
$
offset
]
;
$
c3
=
$
stmt
[
++
$
offset
]
;
$
c4
=
$
stmt
[
++
$
offset
]
;
$
c5
=
$
stmt
[
++
$
offset
]
;
$
c6
=
$
stmt
[
++
$
offset
]
;
if
(
(
$
c1
==
'E'
||
$
c1
==
'e'
)
&&
(
$
c2
==
'L'
||
$
c2
==
'l'
)
&&
(
$
c3
==
'E'
||
$
c3
==
'e'
)
&&
(
$
c4
==
'T'
||
$
c4
==
't'
)
&&
(
$
c5
==
'E'
||
$
c5
==
'e'
)
&&
(
$
c6
==
' '
||
$
c6
==
'\t'
||
$
c6
==
'\r'
||
$
c6
==
'\n'
)
)
{
return
self
::
DELETE
;
}
}
return
self
::
OTHER
;
}


public static function readLength(array $data)
    {
        $length = $data[0];
        switch ($length) {
            case 251:
                return MySQLMessage::$NULL_LENGTH;
            case 252:
                return self::readUB2($data);
            case 253:
                return self::readUB3($data);
            case 254:
                return self::readLong($data);
            default:
                return $length;
        }
    }

public static function readLength ( array $ data ) { $ length = $ data [ 0 ] ; switch ( $ length ) { case 251 : return MySQLMessage :: $ NULL_LENGTH ; case 252 : return self :: readUB2 ( $ data ) ; case 253 : return self :: readUB3 ( $ data ) ; case 254 : return self :: readLong ( $ data ) ; default : return $ length ; } }

this is for the String.

@param array $data

@return int|mixed


public static function build(OutputInterface $output, $action, $index, $type, $offset)
    {
        $progress = null;

        return function ($increment, $totalObjects, $message = null) use (&$progress, $output, $action, $index, $type, $offset) {
            if (null === $progress) {
                $progress = new ProgressBar($output, $totalObjects);
                $progress->start();
                $progress->setProgress($offset);
            }

            if (null !== $message) {
                $progress->clear();
                $output->writeln(sprintf('<info>%s</info> <error>%s</error>', $action, $message));
                $progress->display();
            }

            $progress->setMessage(sprintf('<info>%s</info> <comment>%s/%s</comment>', $action, $index, $type));
            $progress->advance($increment);
        };
    }

Builds a loggerClosure to be called from inside the Provider to update the command
line.

@param OutputInterface $output
@param string          $action
@param string          $index
@param string          $type
@param int             $offset

@return callable

Builds a loggerClosure to be called from inside the Provider to update the command line .


private function log(BulkException $e)
    {
        if (!$this->logger) {
            throw $e;
        }

        $this->logger->error($e);
    }

private function log ( BulkException $ e ) { if ( ! $ this -> logger ) { throw $ e ; } $ this -> logger -> error ( $ e ) ; }

Log exception if logger defined for persister belonging to the current listener, otherwise re-throw.

@param BulkException $e

@throws BulkException

Log exception if logger defined for persister belonging to the current listener otherwise re - throw .


protected function getTypes($config)
    {
        $types = array();

        if (isset($config['types'])) {
            foreach ($config['types'] as $typeConfig) {
                $types[$typeConfig['name']] = new TypeConfig(
                    $typeConfig['name'],
                    $typeConfig['mapping'],
                    $typeConfig['config']
                );
            }
        }

        return $types;
    }

protected function getTypes ( $ config ) { $ types = array ( ) ; if ( isset ( $ config [ 'types' ] ) ) { foreach ( $ config [ 'types' ] as $ typeConfig ) { $ types [ $ typeConfig [ 'name' ] ] = new TypeConfig ( $ typeConfig [ 'name' ] , $ typeConfig [ 'mapping' ] , $ typeConfig [ 'config' ] ) ; } } return $ types ; }

Builds TypeConfig objects for each type.

@param array $config

@return array

Builds TypeConfig objects for each type .


public function getConfiguration()
    {
        $indexes = [];
        foreach ($this->configArray as $config) {
            $types = $this->getTypes($config);
            $index = new IndexConfig($config['name'], $types, [
                'elasticSearchName' => $config['elasticsearch_name'],
                'settings' => $config['settings'],
                'useAlias' => $config['use_alias'],
            ]);

            $indexes[$config['name']] = $index;
        }

        return $indexes;
    }

public function getConfiguration ( ) { $ indexes = [ ] ; foreach ( $ this -> configArray as $ config ) { $ types = $ this -> getTypes ( $ config ) ; $ index = new IndexConfig ( $ config [ 'name' ] , $ types , [ 'elasticSearchName' => $ config [ 'elasticsearch_name' ] , 'settings' => $ config [ 'settings' ] , 'useAlias' => $ config [ 'use_alias' ] , ] ) ; $ indexes [ $ config [ 'name' ] ] = $ index ; } return $ indexes ; }

Should return all configuration available from the data source.

@return IndexConfig[]

Should return all configuration available from the data source .


public function getAllProviders()
    {
        $providers = [];

        foreach ($this->providers as $index => $indexProviders) {
            foreach ($indexProviders as $type => $providerId) {
                $providers[sprintf('%s/%s', $index, $type)] = $this->container->get($providerId);
            }
        }

        return $providers;
    }

public function getAllProviders ( ) { $ providers = [ ] ; foreach ( $ this -> providers as $ index => $ indexProviders ) { foreach ( $ indexProviders as $ type => $ providerId ) { $ providers [ sprintf ( '%s/%s' , $ index , $ type ) ] = $ this -> container -> get ( $ providerId ) ; } } return $ providers ; }

Gets all registered providers.

Providers will be indexed by "index/type" strings in the returned array.

@return PagerProviderInterface[]


public function getIndexProviders($index)
    {
        if (!isset($this->providers[$index])) {
            throw new \InvalidArgumentException(sprintf('No providers were registered for index "%s".', $index));
        }

        $providers = [];
        foreach ($this->providers[$index] as $type => $providerId) {
            $providers[$type] = $this->getProvider($index, $type);
        }

        return $providers;
    }

Gets all providers for an index.

Providers will be indexed by "type" strings in the returned array.

@param string $index

@return PagerProviderInterface[]|

@throws \InvalidArgumentException if no providers were registered for the index


public function getProvider($index, $type)
    {
        if (!isset($this->providers[$index][$type])) {
            throw new \InvalidArgumentException(sprintf('No provider was registered for index "%s" and type "%s".', $index, $type));
        }

        return $this->container->get($this->providers[$index][$type]);
    }

Gets the provider for an index and type.

@param string $index
@param string $type

@return PagerProviderInterface

@throws \InvalidArgumentException if no provider was registered for the index and type

Gets the provider for an index and type .


protected function setSorting(ItemsEvent $event)
    {
        $options = $event->options;
        $sortField = $this->getRequest()->get($options['sortFieldParameterName']);

        if (!$sortField && isset($options['defaultSortFieldName'])) {
            $sortField = $options['defaultSortFieldName'];
        }

        if (!empty($sortField)) {
            $event->target->getQuery()->setSort([
                $sortField => $this->getSort($sortField, $options),
            ]);
        }
    }

protected function setSorting ( ItemsEvent $ event ) { $ options = $ event -> options ; $ sortField = $ this -> getRequest ( ) -> get ( $ options [ 'sortFieldParameterName' ] ) ; if ( ! $ sortField && isset ( $ options [ 'defaultSortFieldName' ] ) ) { $ sortField = $ options [ 'defaultSortFieldName' ] ; } if ( ! empty ( $ sortField ) ) { $ event -> target -> getQuery ( ) -> setSort ( [ $ sortField => $ this -> getSort ( $ sortField , $ options ) , ] ) ; } }

Adds knp paging sort to query.

@param ItemsEvent $event


public function buildIndexMapping(IndexConfigInterface $indexConfig)
    {
        $typeMappings = [];
        foreach ($indexConfig->getTypes() as $typeConfig) {
            $typeMappings[$typeConfig->getName()] = $this->buildTypeMapping($typeConfig);
        }

        $mapping = [];
        if (!empty($typeMappings)) {
            $mapping['mappings'] = $typeMappings;
        }
        // 'warmers' => $indexConfig->getWarmers(),

        $settings = $indexConfig->getSettings();
        if (!empty($settings)) {
            $mapping['settings'] = $settings;
        }

        return $mapping;
    }

Builds mappings for an entire index.

@param IndexConfigInterface $indexConfig

@return array


public function buildIndexTemplateMapping(IndexTemplateConfig $indexTemplateConfig)
    {
        $mapping = $this->buildIndexMapping($indexTemplateConfig);
        $mapping['template'] = $indexTemplateConfig->getTemplate();

        return $mapping;
    }

public function buildIndexTemplateMapping ( IndexTemplateConfig $ indexTemplateConfig ) { $ mapping = $ this -> buildIndexMapping ( $ indexTemplateConfig ) ; $ mapping [ 'template' ] = $ indexTemplateConfig -> getTemplate ( ) ; return $ mapping ; }

Builds mappings for an entire index template.

@param IndexTemplateConfig $indexTemplateConfig

@return array

Builds mappings for an entire index template .


public function buildTypeMapping(TypeConfig $typeConfig)
    {
        $mapping = $typeConfig->getMapping();

        if (null !== $typeConfig->getDynamicDateFormats()) {
            $mapping['dynamic_date_formats'] = $typeConfig->getDynamicDateFormats();
        }

        if (null !== $typeConfig->getDateDetection()) {
            $mapping['date_detection'] = $typeConfig->getDateDetection();
        }

        if (null !== $typeConfig->getNumericDetection()) {
            $mapping['numeric_detection'] = $typeConfig->getNumericDetection();
        }

        if ($typeConfig->getAnalyzer()) {
            $mapping['analyzer'] = $typeConfig->getAnalyzer();
        }

        if (null !== $typeConfig->getDynamic()) {
            $mapping['dynamic'] = $typeConfig->getDynamic();
        }

        if (isset($mapping['dynamic_templates']) and empty($mapping['dynamic_templates'])) {
            unset($mapping['dynamic_templates']);
        }

        $this->fixProperties($mapping['properties']);
        if (!$mapping['properties']) {
            unset($mapping['properties']);
        }

        if ($typeConfig->getModel()) {
            $mapping['_meta']['model'] = $typeConfig->getModel();
        }

        unset($mapping['_parent']['identifier'], $mapping['_parent']['property']);

        if (empty($mapping)) {
            // Empty mapping, we want it encoded as a {} instead of a []
            $mapping = new \ArrayObject();
        }

        return $mapping;
    }

Builds mappings for a single type.

@param TypeConfig $typeConfig

@return array


private function fixProperties(&$properties)
    {
        foreach ($properties as $name => &$property) {
            unset($property['property_path']);

            if (!isset($property['type'])) {
                $property['type'] = 'text';
            }
            if (isset($property['fields'])) {
                $this->fixProperties($property['fields']);
            }
            if (isset($property['properties'])) {
                $this->fixProperties($property['properties']);
            }
        }
    }

private function fixProperties ( & $ properties ) { foreach ( $ properties as $ name => & $ property ) { unset ( $ property [ 'property_path' ] ) ; if ( ! isset ( $ property [ 'type' ] ) ) { $ property [ 'type' ] = 'text' ; } if ( isset ( $ property [ 'fields' ] ) ) { $ this -> fixProperties ( $ property [ 'fields' ] ) ; } if ( isset ( $ property [ 'properties' ] ) ) { $ this -> fixProperties ( $ property [ 'properties' ] ) ; } } }

Fixes any properties and applies basic defaults for any field that does not have
required options.

@param $properties

Fixes any properties and applies basic defaults for any field that does not have required options .


public function logQuery($path, $method, $data, $queryTime, $connection = [], $query = [], $engineTime = 0, $itemCount = 0)
    {
        $executionMS = $queryTime * 1000;

        if ($this->debug) {
            $e = new \Exception();
            if (is_string($data)) {
                $jsonStrings = explode("\n", $data);
                $data = [];
                foreach ($jsonStrings as $json) {
                    if ($json != '') {
                        $data[] = json_decode($json, true);
                    }
                }
            } else {
                $data = [$data];
            }

            $this->queries[] = [
                'path' => $path,
                'method' => $method,
                'data' => $data,
                'executionMS' => $executionMS,
                'engineMS' => $engineTime,
                'connection' => $connection,
                'queryString' => $query,
                'itemCount' => $itemCount,
                'backtrace' => $e->getTraceAsString(),
            ];
        }

        if (null !== $this->logger) {
            $message = sprintf('%s (%s) %0.2f ms', $path, $method, $executionMS);
            $this->logger->info($message, (array) $data);
        }
    }

public
function
logQuery
(
$
path
,
$
method
,
$
data
,
$
queryTime
,
$
connection
=
[
]
,
$
query
=
[
]
,
$
engineTime
=
0
,
$
itemCount
=
0
)
{
$
executionMS
=
$
queryTime
*
1000
;
if
(
$
this
->
debug
)
{
$
e
=
new
\
Exception
(
)
;
if
(
is_string
(
$
data
)
)
{
$
jsonStrings
=
explode
(
"\n"
,
$
data
)
;
$
data
=
[
]
;
foreach
(
$
jsonStrings
as
$
json
)
{
if
(
$
json
!=
''
)
{
$
data
[
]
=
json_decode
(
$
json
,
true
)
;
}
}
}
else
{
$
data
=
[
$
data
]
;
}
$
this
->
queries
[
]
=
[
'path'
=>
$
path
,
'method'
=>
$
method
,
'data'
=>
$
data
,
'executionMS'
=>
$
executionMS
,
'engineMS'
=>
$
engineTime
,
'connection'
=>
$
connection
,
'queryString'
=>
$
query
,
'itemCount'
=>
$
itemCount
,
'backtrace'
=>
$
e
->
getTraceAsString
(
)
,
]
;
}
if
(
!==
$
this
->
logger
)
{
$
message
=
sprintf
(
'%s (%s) %0.2f ms'
,
$
path
,
$
method
,
$
executionMS
)
;
$
this
->
logger
->
info
(
$
message
,
(
array
)
$
data
)
;
}
}

Logs a query.

@param string $path       Path to call
@param string $method     Rest method to use (GET, POST, DELETE, PUT)
@param array|string $data Arguments
@param float  $queryTime  Execution time (in seconds)
@param array  $connection Host, port, transport, and headers of the query
@param array  $query      Arguments
@param int    $engineTime
@param int    $itemCount


public function transformToElasticaDocument($object)
    {
        $document = $this->transformer->transform($object, []);

        $data = call_user_func($this->serializer, $object);
        $document->setData($data);

        return $document;
    }

public function transformToElasticaDocument ( $ object ) { $ document = $ this -> transformer -> transform ( $ object , [ ] ) ; $ data = call_user_func ( $ this -> serializer , $ object ) ; $ document -> setData ( $ data ) ; return $ document ; }

Transforms an object to an elastica document
with just the identifier set.

@param object $object

@return Document the elastica document

Transforms an object to an elastica document with just the identifier set .


public function getTotalHits($genuineTotal = false)
    {
        if (!isset($this->totalHits)) {
            $this->totalHits = $this->searchable->count($this->query);
        }

        return $this->query->hasParam('size') && !$genuineTotal
            ? min($this->totalHits, (int) $this->query->getParam('size'))
            : $this->totalHits;
    }

public function getTotalHits ( $ genuineTotal = false ) { if ( ! isset ( $ this -> totalHits ) ) { $ this -> totalHits = $ this -> searchable -> count ( $ this -> query ) ; } return $ this -> query -> hasParam ( 'size' ) && ! $ genuineTotal ? min ( $ this -> totalHits , ( int ) $ this -> query -> getParam ( 'size' ) ) : $ this -> totalHits ; }

Returns the number of results.

If genuineTotal is provided as true, total hits is returned from the
hits.total value from the search results instead of just returning
the requested size.

{@inheritdoc}


protected function getElasticaResults($offset, $itemCountPerPage)
    {
        $offset = (int) $offset;
        $itemCountPerPage = (int) $itemCountPerPage;
        $size = $this->query->hasParam('size')
            ? (int) $this->query->getParam('size')
            : null;

        if (null !== $size && $size < $offset + $itemCountPerPage) {
            $itemCountPerPage = $size - $offset;
        }

        if ($itemCountPerPage < 1) {
            throw new InvalidArgumentException('$itemCountPerPage must be greater than zero');
        }

        $query = clone $this->query;
        $query->setFrom($offset);
        $query->setSize($itemCountPerPage);

        $resultSet = $this->searchable->search($query, $this->options);
        $this->totalHits = $resultSet->getTotalHits();
        $this->aggregations = $resultSet->getAggregations();
        $this->suggests = $resultSet->getSuggests();
        $this->maxScore = $resultSet->getMaxScore();

        return $resultSet;
    }

Returns the paginated results.

@param int $offset
@param int $itemCountPerPage

@throws \InvalidArgumentException

@return ResultSet


public function deleteTemplateIndexes(IndexTemplateConfig $template)
    {
        $this->client->request($template->getTemplate() . '/', Request::DELETE);
    }

public function deleteTemplateIndexes ( IndexTemplateConfig $ template ) { $ this -> client -> request ( $ template -> getTemplate ( ) . '/' , Request :: DELETE ) ; }

Delete all template indexes

@param IndexTemplateConfig $template


public function getIndex($name = null)
    {
        if (null === $name) {
            return $this->defaultIndex;
        }

        if (!isset($this->indexes[$name])) {
            throw new \InvalidArgumentException(sprintf('The index "%s" does not exist', $name));
        }

        return $this->indexes[$name];
    }

public
function
getIndex
(
$
name
=
)
{
if
(
===
$
name
)
{
return
$
this
->
defaultIndex
;
}
if
(
!
isset
(
$
this
->
indexes
[
$
name
]
)
)
{
throw
new
\
InvalidArgumentException
(
sprintf
(
'The index "%s" does not exist'
,
$
name
)
)
;
}
return
$
this
->
indexes
[
$
name
]
;
}

Gets an index by its name.

@param string $name Index to return, or the default index if null

@return Index

@throws \InvalidArgumentException if no index exists for the given name


public function getPersister($index, $type)
    {
        if (!isset($this->persisters[$index][$type])) {
            throw new \InvalidArgumentException(sprintf('No persister was registered for index "%s" and type "%s".', $index, $type));
        }

        return $this->container->get($this->persisters[$index][$type]);
    }

Gets the persister for an index and type.

@param string $index
@param string $type

@return ObjectPersisterInterface

@throws \InvalidArgumentException if no persister was registered for the index and type

Gets the persister for an index and type .


private function populateIndex(OutputInterface $output, $index, $reset, $options)
    {
        $event = new IndexPopulateEvent($index, $reset, $options);
        $this->dispatcher->dispatch(IndexPopulateEvent::PRE_INDEX_POPULATE, $event);

        if ($event->isReset()) {
            $output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
            $this->resetter->resetIndex($index, true);
        }

        $types = array_keys($this->pagerProviderRegistry->getIndexProviders($index));
        foreach ($types as $type) {
            $this->populateIndexType($output, $index, $type, false, $event->getOptions());
        }

        $this->dispatcher->dispatch(IndexPopulateEvent::POST_INDEX_POPULATE, $event);

        $this->refreshIndex($output, $index);
    }

Recreates an index, populates its types, and refreshes the index.

@param OutputInterface $output
@param string          $index
@param bool            $reset
@param array           $options

Recreates an index populates its types and refreshes the index .


private function refreshIndex(OutputInterface $output, $index)
    {
        $output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
        $this->indexManager->getIndex($index)->refresh();
    }

Refreshes an index.

@param OutputInterface $output
@param string          $index


public function transform($object, array $fields)
    {
        $identifier = $this->propertyAccessor->getValue($object, $this->options['identifier']);
        if ($identifier && !is_scalar($identifier)) {
            $identifier = (string) $identifier;
        }

        return $this->transformObjectToDocument($object, $fields, $identifier);
    }

public function transform ( $ object , array $ fields ) { $ identifier = $ this -> propertyAccessor -> getValue ( $ object , $ this -> options [ 'identifier' ] ) ; if ( $ identifier && ! is_scalar ( $ identifier ) ) { $ identifier = ( string ) $ identifier ; } return $ this -> transformObjectToDocument ( $ object , $ fields , $ identifier ) ; }

Transforms an object into an elastica object having the required keys.

@param object $object the object to convert
@param array  $fields the keys we want to have in the returned array

@return Document

Transforms an object into an elastica object having the required keys .


protected function transformNested($objects, array $fields)
    {
        if (is_array($objects) || $objects instanceof \Traversable || $objects instanceof \ArrayAccess) {
            $documents = [];
            foreach ($objects as $object) {
                $document = $this->transformObjectToDocument($object, $fields);
                $documents[] = $document->getData();
            }

            return $documents;
        } elseif (null !== $objects) {
            $document = $this->transformObjectToDocument($objects, $fields);

            return $document->getData();
        }

        return [];
    }

protected function transformNested ( $ objects , array $ fields ) { if ( is_array ( $ objects ) || $ objects instanceof \ Traversable || $ objects instanceof \ ArrayAccess ) { $ documents = [ ] ; foreach ( $ objects as $ object ) { $ document = $ this -> transformObjectToDocument ( $ object , $ fields ) ; $ documents [ ] = $ document -> getData ( ) ; } return $ documents ; } elseif ( null !== $ objects ) { $ document = $ this -> transformObjectToDocument ( $ objects , $ fields ) ; return $ document -> getData ( ) ; } return [ ] ; }

transform a nested document or an object property into an array of ElasticaDocument.

@param array|\Traversable|\ArrayAccess $objects the object to convert
@param array                           $fields  the keys we want to have in the returned array

@return array

transform a nested document or an object property into an array of ElasticaDocument .


protected function normalizeValue($value)
    {
        $normalizeValue = function (&$v) {
            if ($v instanceof \DateTimeInterface) {
                $v = $v->format('c');
            } elseif (!is_scalar($v) && !is_null($v)) {
                $v = (string) $v;
            }
        };

        if (is_array($value) || $value instanceof \Traversable || $value instanceof \ArrayAccess) {
            $value = is_array($value) ? $value : iterator_to_array($value, false);
            array_walk_recursive($value, $normalizeValue);
        } else {
            $normalizeValue($value);
        }

        return $value;
    }

protected function normalizeValue ( $ value ) { $ normalizeValue = function ( & $ v ) { if ( $ v instanceof \ DateTimeInterface ) { $ v = $ v -> format ( 'c' ) ; } elseif ( ! is_scalar ( $ v ) && ! is_null ( $ v ) ) { $ v = ( string ) $ v ; } } ; if ( is_array ( $ value ) || $ value instanceof \ Traversable || $ value instanceof \ ArrayAccess ) { $ value = is_array ( $ value ) ? $ value : iterator_to_array ( $ value , false ) ; array_walk_recursive ( $ value , $ normalizeValue ) ; } else { $ normalizeValue ( $ value ) ; } return $ value ; }

Attempts to convert any type to a string or an array of strings.

@param mixed $value

@return string|array

Attempts to convert any type to a string or an array of strings .


protected function transformObjectToDocument($object, array $fields, $identifier = '')
    {
        $document = new Document($identifier, [], '', $this->options['index']);

        if ($this->dispatcher) {
            $event = new TransformEvent($document, $fields, $object);
            $this->dispatcher->dispatch(TransformEvent::PRE_TRANSFORM, $event);

            $document = $event->getDocument();
        }

        foreach ($fields as $key => $mapping) {
            if ('_parent' == $key) {
                $property = (null !== $mapping['property']) ? $mapping['property'] : $mapping['type'];
                $value = $this->propertyAccessor->getValue($object, $property);
                $document->setParent($this->propertyAccessor->getValue($value, $mapping['identifier']));

                continue;
            }

            $path = isset($mapping['property_path']) ?
                $mapping['property_path'] :
                $key;
            if (false === $path) {
                continue;
            }
            $value = $this->propertyAccessor->getValue($object, $path);

            if (isset($mapping['type']) && in_array(
                    $mapping['type'], ['nested', 'object']
                ) && isset($mapping['properties']) && !empty($mapping['properties'])
            ) {
                /* $value is a nested document or object. Transform $value into
                 * an array of documents, respective the mapped properties.
                 */
                $document->set($key, $this->transformNested($value, $mapping['properties']));

                continue;
            }

            if (isset($mapping['type']) && 'attachment' == $mapping['type']) {
                // $value is an attachment. Add it to the document.
                if ($value instanceof \SplFileInfo) {
                    $document->addFile($key, $value->getPathName());
                } else {
                    $document->addFileContent($key, $value);
                }

                continue;
            }

            $document->set($key, $this->normalizeValue($value));
        }

        if ($this->dispatcher) {
            $event = new TransformEvent($document, $fields, $object);
            $this->dispatcher->dispatch(TransformEvent::POST_TRANSFORM, $event);

            $document = $event->getDocument();
        }

        return $document;
    }

Transforms the given object to an elastica document.

@param object $object     the object to convert
@param array  $fields     the keys we want to have in the returned array
@param string $identifier the identifier for the new document

@return Document

Transforms the given object to an elastica document .


protected function getEntityQueryBuilder()
    {
        $repository = $this->registry
            ->getManagerForClass($this->objectClass)
            ->getRepository($this->objectClass);

        return $repository->{$this->options['query_builder_method']}(static::ENTITY_ALIAS);
    }

protected function getEntityQueryBuilder ( ) { $ repository = $ this -> registry -> getManagerForClass ( $ this -> objectClass ) -> getRepository ( $ this -> objectClass ) ; return $ repository -> { $ this -> options [ 'query_builder_method' ] } ( static :: ENTITY_ALIAS ) ; }

Retrieves a query builder to be used for querying by identifiers.

@return \Doctrine\ORM\QueryBuilder

Retrieves a query builder to be used for querying by identifiers .


public function getIndexTemplate($name)
    {
        if (!isset($this->templates[$name])) {
            throw new \InvalidArgumentException(sprintf('The index template "%s" does not exist', $name));
        }

        return $this->templates[$name];
    }

Gets an index template by its name.

@param string $name Index template to return

@return IndexTemplate

@throws \InvalidArgumentException if no index template exists for the given name


public function resetAllIndexes($populating = false, $force = false)
    {
        foreach ($this->configManager->getIndexNames() as $name) {
            $this->resetIndex($name, $populating, $force);
        }
    }

public function resetAllIndexes ( $ populating = false , $ force = false ) { foreach ( $ this -> configManager -> getIndexNames ( ) as $ name ) { $ this -> resetIndex ( $ name , $ populating , $ force ) ; } }

Deletes and recreates all indexes.

@param bool $populating
@param bool $force


public function resetIndex($indexName, $populating = false, $force = false)
    {
        $indexConfig = $this->configManager->getIndexConfiguration($indexName);
        $index = $this->indexManager->getIndex($indexName);

        if ($indexConfig->isUseAlias()) {
            $this->aliasProcessor->setRootName($indexConfig, $index);
        }

        $event = new IndexResetEvent($indexName, $populating, $force);
        $this->dispatcher->dispatch(IndexResetEvent::PRE_INDEX_RESET, $event);

        $mapping = $this->mappingBuilder->buildIndexMapping($indexConfig);
        $index->create($mapping, true);

        if (!$populating and $indexConfig->isUseAlias()) {
            $this->aliasProcessor->switchIndexAlias($indexConfig, $index, $force);
        }

        $this->dispatcher->dispatch(IndexResetEvent::POST_INDEX_RESET, $event);
    }

public function resetIndex ( $ indexName , $ populating = false , $ force = false ) { $ indexConfig = $ this -> configManager -> getIndexConfiguration ( $ indexName ) ; $ index = $ this -> indexManager -> getIndex ( $ indexName ) ; if ( $ indexConfig -> isUseAlias ( ) ) { $ this -> aliasProcessor -> setRootName ( $ indexConfig , $ index ) ; } $ event = new IndexResetEvent ( $ indexName , $ populating , $ force ) ; $ this -> dispatcher -> dispatch ( IndexResetEvent :: PRE_INDEX_RESET , $ event ) ; $ mapping = $ this -> mappingBuilder -> buildIndexMapping ( $ indexConfig ) ; $ index -> create ( $ mapping , true ) ; if ( ! $ populating and $ indexConfig -> isUseAlias ( ) ) { $ this -> aliasProcessor -> switchIndexAlias ( $ indexConfig , $ index , $ force ) ; } $ this -> dispatcher -> dispatch ( IndexResetEvent :: POST_INDEX_RESET , $ event ) ; }

Deletes and recreates the named index. If populating, creates a new index
with a randomised name for an alias to be set after population.

@param string $indexName
@param bool   $populating
@param bool   $force      If index exists with same name as alias, remove it

@throws \InvalidArgumentException if no index exists for the given name

Deletes and recreates the named index . If populating creates a new index with a randomised name for an alias to be set after population .


public function resetIndexType($indexName, $typeName)
    {
        $typeConfig = $this->configManager->getTypeConfiguration($indexName, $typeName);

        $this->resetIndex($indexName, true);

        $index = $this->indexManager->getIndex($indexName);
        $type = $index->getType($typeName);

        $event = new TypeResetEvent($indexName, $typeName);
        $this->dispatcher->dispatch(TypeResetEvent::PRE_TYPE_RESET, $event);

        $mapping = new Mapping();
        foreach ($this->mappingBuilder->buildTypeMapping($typeConfig) as $name => $field) {
            $mapping->setParam($name, $field);
        }

        $type->setMapping($mapping);

        $this->dispatcher->dispatch(TypeResetEvent::POST_TYPE_RESET, $event);
    }

public function resetIndexType ( $ indexName , $ typeName ) { $ typeConfig = $ this -> configManager -> getTypeConfiguration ( $ indexName , $ typeName ) ; $ this -> resetIndex ( $ indexName , true ) ; $ index = $ this -> indexManager -> getIndex ( $ indexName ) ; $ type = $ index -> getType ( $ typeName ) ; $ event = new TypeResetEvent ( $ indexName , $ typeName ) ; $ this -> dispatcher -> dispatch ( TypeResetEvent :: PRE_TYPE_RESET , $ event ) ; $ mapping = new Mapping ( ) ; foreach ( $ this -> mappingBuilder -> buildTypeMapping ( $ typeConfig ) as $ name => $ field ) { $ mapping -> setParam ( $ name , $ field ) ; } $ type -> setMapping ( $ mapping ) ; $ this -> dispatcher -> dispatch ( TypeResetEvent :: POST_TYPE_RESET , $ event ) ; }

Deletes and recreates a mapping type for the named index.

@param string $indexName
@param string $typeName

@throws \InvalidArgumentException if no index or type mapping exists for the given names
@throws ResponseException

Deletes and recreates a mapping type for the named index .


public function switchIndexAlias($indexName, $delete = true)
    {
        $indexConfig = $this->configManager->getIndexConfiguration($indexName);

        if ($indexConfig->isUseAlias()) {
            $index = $this->indexManager->getIndex($indexName);
            $this->aliasProcessor->switchIndexAlias($indexConfig, $index, false, $delete);
        }
    }

public function switchIndexAlias ( $ indexName , $ delete = true ) { $ indexConfig = $ this -> configManager -> getIndexConfiguration ( $ indexName ) ; if ( $ indexConfig -> isUseAlias ( ) ) { $ index = $ this -> indexManager -> getIndex ( $ indexName ) ; $ this -> aliasProcessor -> switchIndexAlias ( $ indexConfig , $ index , false , $ delete ) ; } }

Switching aliases.

@param string $indexName
@param bool   $delete    Delete or close index

@throws \FOS\ElasticaBundle\Exception\AliasIsIndexException


private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index)
    {
        /* Note: transformer services may conflict with "collection.index", if
         * an index and type names were "collection" and an index, respectively.
         */
        $transformerId = sprintf('fos_elastica.elastica_to_model_transformer.collection.%s', $name);
        $transformerDef = new ChildDefinition('fos_elastica.elastica_to_model_transformer.collection');
        $container->setDefinition($transformerId, $transformerDef);

        $finderId = sprintf('fos_elastica.finder.%s', $name);
        $finderDef = new ChildDefinition('fos_elastica.finder');
        $finderDef->replaceArgument(0, $index);
        $finderDef->replaceArgument(1, new Reference($transformerId));

        $container->setDefinition($finderId, $finderDef);
    }

Loads the configured index finders.

@param \Symfony\Component\DependencyInjection\ContainerBuilder $container
@param string                                                  $name      The index name
@param Reference                                               $index     Reference to the related index

@return string


private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $typeName)
    {
        if (isset($typeConfig['driver'])) {
            $this->loadDriver($container, $typeConfig['driver']);
        }

        $elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName);
        $modelToElasticaTransformerId = $this->loadModelToElasticaTransformer($typeConfig, $container, $indexName, $typeName);
        $objectPersisterId = $this->loadObjectPersister($typeConfig, $typeRef, $container, $indexName, $typeName, $modelToElasticaTransformerId);

        if (isset($typeConfig['provider'])) {
            $this->loadTypePagerProvider($typeConfig, $container, $indexName, $typeName);
        }
        if (isset($typeConfig['finder'])) {
            $this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeRef, $indexName, $typeName);
        }
        if (isset($typeConfig['listener']) && $typeConfig['listener']['enabled']) {
            $this->loadTypeListener($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
        }
    }

private function loadTypePersistenceIntegration ( array $ typeConfig , ContainerBuilder $ container , Reference $ typeRef , $ indexName , $ typeName ) { if ( isset ( $ typeConfig [ 'driver' ] ) ) { $ this -> loadDriver ( $ container , $ typeConfig [ 'driver' ] ) ; } $ elasticaToModelTransformerId = $ this -> loadElasticaToModelTransformer ( $ typeConfig , $ container , $ indexName , $ typeName ) ; $ modelToElasticaTransformerId = $ this -> loadModelToElasticaTransformer ( $ typeConfig , $ container , $ indexName , $ typeName ) ; $ objectPersisterId = $ this -> loadObjectPersister ( $ typeConfig , $ typeRef , $ container , $ indexName , $ typeName , $ modelToElasticaTransformerId ) ; if ( isset ( $ typeConfig [ 'provider' ] ) ) { $ this -> loadTypePagerProvider ( $ typeConfig , $ container , $ indexName , $ typeName ) ; } if ( isset ( $ typeConfig [ 'finder' ] ) ) { $ this -> loadTypeFinder ( $ typeConfig , $ container , $ elasticaToModelTransformerId , $ typeRef , $ indexName , $ typeName ) ; } if ( isset ( $ typeConfig [ 'listener' ] ) && $ typeConfig [ 'listener' ] [ 'enabled' ] ) { $ this -> loadTypeListener ( $ typeConfig , $ container , $ objectPersisterId , $ indexName , $ typeName ) ; } }

Loads the optional provider and finder for a type.

@param array            $typeConfig
@param ContainerBuilder $container
@param Reference        $typeRef
@param string           $indexName
@param string           $typeName

Loads the optional provider and finder for a type .


private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
    {
        if (isset($typeConfig['elastica_to_model_transformer']['service'])) {
            return $typeConfig['elastica_to_model_transformer']['service'];
        }

        /* Note: transformer services may conflict with "prototype.driver", if
         * the index and type names were "prototype" and a driver, respectively.
         */
        $abstractId = sprintf('fos_elastica.elastica_to_model_transformer.prototype.%s', $typeConfig['driver']);
        $serviceId = sprintf('fos_elastica.elastica_to_model_transformer.%s.%s', $indexName, $typeName);
        $serviceDef = new ChildDefinition($abstractId);
        $serviceDef->addTag('fos_elastica.elastica_to_model_transformer', ['type' => $typeName, 'index' => $indexName]);

        $serviceDef->replaceArgument(1, $typeConfig['model']);
        $serviceDef->replaceArgument(2, array_merge($typeConfig['elastica_to_model_transformer'], [
            'identifier' => $typeConfig['identifier'],
        ]));
        $container->setDefinition($serviceId, $serviceDef);

        return $serviceId;
    }

Creates and loads an ElasticaToModelTransformer.

@param array            $typeConfig
@param ContainerBuilder $container
@param string           $indexName
@param string           $typeName

@return string

Creates and loads an ElasticaToModelTransformer .


private function loadObjectPersister(array $typeConfig, Reference $typeRef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
    {
        if (isset($typeConfig['persister']['service'])) {
            return $typeConfig['persister']['service'];
        }

        $arguments = [
            $typeRef,
            new Reference($transformerId),
            $typeConfig['model'],
        ];

        if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
            $abstractId = 'fos_elastica.object_serializer_persister';
            $callbackId = sprintf('%s.%s.serializer.callback', $this->indexConfigs[$indexName]['reference'], $typeName);
            $arguments[] = [new Reference($callbackId), 'serialize'];
        } else {
            $abstractId = 'fos_elastica.object_persister';
            $mapping = $this->indexConfigs[$indexName]['types'][$typeName]['mapping'];
            $argument = $mapping['properties'];
            if (isset($mapping['_parent'])) {
                $argument['_parent'] = $mapping['_parent'];
            }
            $arguments[] = $argument;
        }

        $arguments[] = array_intersect_key($typeConfig['persister'], array_flip(['refresh']));

        $serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
        $serviceDef = new ChildDefinition($abstractId);
        foreach ($arguments as $i => $argument) {
            $serviceDef->replaceArgument($i, $argument);
        }

        $serviceDef->addTag('fos_elastica.persister', ['index' => $indexName, 'type' => $typeName]);

        $container->setDefinition($serviceId, $serviceDef);

        return $serviceId;
    }

Creates and loads an object persister for a type.

@param array            $typeConfig
@param Reference        $typeRef
@param ContainerBuilder $container
@param string           $indexName
@param string           $typeName
@param string           $transformerId

@return string

Creates and loads an object persister for a type .