Working with Taxonomies

  1. Create a Category Entity

     

    namespace App\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\Common\Collections\Collection;
    
    use Sylius\Component\Resource\Model\ResourceInterface;
    use Vankosoft\ApplicationBundle\Model\Interfaces\TaxonInterface;
    
    /**
     * @ORM\Table(name="VSORG_ProjectCategories")
     * @ORM\Entity
     */
    class ProjectCategory implements ResourceInterface
    {
        /**
         * @var integer
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="IDENTITY")
         */
        private $id;
        
        /**
         * @var TaxonInterface
         *
         * @ORM\OneToOne(targetEntity="App\Entity\Application\Taxon", cascade={"all"}, orphanRemoval=true)
         * @ORM\JoinColumn(name="taxon_id", referencedColumnName="id", nullable=false)
         */
        private $taxon;
        
        /**
         * @var ProjectCategory
         *
         * @ORM\ManyToOne(targetEntity="App\Entity\ProjectCategory", inversedBy="children", cascade={"all"})
         */
        private $parent;
        
        /**
         * @var Collection|ProjectCategory[]
         *
         * @ORM\OneToMany(targetEntity="App\Entity\ProjectCategory", mappedBy="parent")
         */
        private $children;
        
        /**
         * @ORM\OneToMany(targetEntity="App\Entity\Project", mappedBy="category", orphanRemoval=true)
         * @ORM\OrderBy({"position" = "ASC"})
         */
        private $projects;
        
        public function __construct()
        {
            $this->children = new ArrayCollection();
            $this->projects = new ArrayCollection();
        }
        
        /**
         * Get id
         *
         * @return integer
         */
        public function getId()
        {
            return $this->id;
        }
        
        /**
         * {@inheritdoc}
         */
        public function getParent(): ?ProjectCategory
        {
            return $this->parent;
        }
        
        /**
         * {@inheritdoc}
         */
        public function setParent( ?ProjectCategory $parent ): self
        {
            $this->parent = $parent;
            
            return $this;
        }
        
        public function getChildren() : Collection
        {
            //return $this->children;
            return $this->taxon->getChildren();
        }
        
        /**
         * @return Collection|Project[]
         */
        public function getProjects(): Collection
        {
            return $this->projects;
        }
        
        public function addProject( Project $project ): self
        {
            if ( ! $this->projects->contains( $project ) ) {
                $this->projects[] = $project;
                $project->addCategory( $this );
            }
            
            return $this;
        }
        
        public function removeProject( Project $project ): self
        {
            if ( ! $this->projects->contains( $project ) ) {
                $this->projects->removeElement( $project );
                $project->removeCategory( $this );
            }
            
            return $this;
        }
        
        /**
         * {@inheritdoc}
         */
        public function getTaxon(): ?TaxonInterface
        {
            return $this->taxon;
        }
        
        /**
         * {@inheritdoc}
         */
        public function setTaxon( ?TaxonInterface $taxon ): self
        {
            $this->taxon = $taxon;
            
            return $this;
        }
        
        public function getName()
        {
            return $this->taxon ? $this->taxon->getName() : '';
        }
        
        public function setName( string $name ): self
        {
            if ( ! $this->taxon ) {
                // Create new taxon into the controller and set the properties passed from form
                return $this;
            }
            $this->taxon->setName( $name );
            
            return $this;
        }
        
        public function __toString()
        {
            return $this->taxon ? $this->taxon->getName() : '';
        }
    }
    
  2. Create a Category Form

    namespace App\Form;
    
    use Vankosoft\ApplicationBundle\Form\AbstractForm;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    
    use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
    use Symfony\Component\Form\Extension\Core\Type\TextType;
    
    use Vankosoft\ApplicationBundle\Component\I18N;
    
    use App\Entity\ProjectCategory;
    
    class ProjectCategoryForm extends AbstractForm
    {    
        public function buildForm( FormBuilderInterface $builder, array $options )
        {
            parent::buildForm( $builder, $options );
            
            $builder
                ->add( 'currentLocale', ChoiceType::class, [
                    'choices'               => \array_flip( I18N::LanguagesAvailable() ),
                    'mapped'                => false,
                    'label'                 => 'vankosoft_org.form.locale',
                    'translation_domain'    => 'VankoSoftOrg',
                ])
                ->add( 'name', TextType::class, [
                    'label'                 => 'vankosoft_org.form.name',
                    'translation_domain'    => 'VankoSoftOrg',
                ])
            ;
        }
        
        public function configureOptions( OptionsResolver $resolver ): void
        {
            parent::configureOptions( $resolver );
            
            $resolver->setDefaults([
                'data_class' => ProjectCategory::class
            ]);
        }
    }
    

     

  3. Create a Category CRUD Controller

    namespace App\Controller\AdminPanel;
    
    use Symfony\Component\HttpFoundation\Request;
    use Vankosoft\ApplicationBundle\Controller\AbstractCrudController;
    use Vankosoft\ApplicationBundle\Controller\TaxonomyHelperTrait;
    
    use App\Entity\Application\Taxon;
    
    class ProjectsCategoryController extends AbstractCrudController
    {
        use TaxonomyHelperTrait;
        
        protected function prepareEntity( &$entity, &$form, Request $request )
        {
            $translatableLocale = $form['currentLocale']->getData();
            $categoryName       = $form['name']->getData();
            
            $parentGroup        = null;
            $entity->setName( $categoryName );
            
            if ( $entity->getTaxon() ) {
                $entityTaxon    = $entity->getTaxon();
                
                if ( ! in_array( $translatableLocale, $entityTaxon->getExistingTranslations() ) ) {
                    $taxonTranslation   = $this->createTranslation( $entityTaxon, $translatableLocale, $categoryName );
                    $entityTaxon->addTranslation( $taxonTranslation );
                }
                
                $entityTaxon->setCurrentLocale( $translatableLocale );
                $entityTaxon->setName( $categoryName );
                $entityTaxon->setCode( $this->get( 'vs_application.slug_generator' )->generate( $categoryName ) );
                
                if ( $parentGroup ) {
                    $entityTaxon->setParent( $parentGroup->getTaxon() );
                }
            } else {
                $taxonomy   = $this->get( 'vs_application.repository.taxonomy' )->findByCode(
                    $this->getParameter( 'vs_org.project-categories.taxonomy_code' )
                );
                
                $entityTaxon    = $this->createTaxon(
                    $categoryName,
                    $translatableLocale,
                    $parentGroup ? $parentGroup->getTaxon() : null,
                    $taxonomy->getId()
                );
            }
            
            $entity->setTaxon( $entityTaxon );
            $entity->setParent( $parentGroup );
            
            $this->getDoctrine()->getManager()->persist( $entityTaxon );
        }
        
        protected function customData( Request $request, $entity = NULL ): array
        {
            $taxonomy   = $this->get( 'vs_application.repository.taxonomy' )->findByCode(
                $this->getParameter( 'vs_org.project-categories.taxonomy_code' )
            );
            
            $translations   = $this->classInfo['action'] == 'indexAction' ? $this->getTranslations() : [];
            
            return [
                'taxonomyId'    => $taxonomy->getId(),
                'translations'  => $translations,
            ];
        }
        
        private function createTranslation( $taxon, $locale, $name )
        {
            $translation    = $taxon->createNewTranslation();
            
            $translation->setLocale( $locale );
            $translation->setName( $name );
            $translation->setSlug( $this->get( 'vs_application.slug_generator' )->generate( $name ) );
            
            return $translation;
        }
        
        private function getTranslations()
        {
            $locales        = $this->get( 'vs_application.repository.locale' )->findAll();
            
            $translations   = [];
            foreach ( $this->resources->getCurrentPageResults() as $category ) {
                foreach( $locales as $locale ) {
                    $category->getTaxon()->getTranslation( $locale->getCode() );
                }
                
                $translations[$category->getId()] = $category->getTaxon()->getExistingTranslations();
            }
            //echo "<pre>"; var_dump($translations); die;
            return $translations;
        }
    }
    

     

  4. Make Resource Configurations

    1. Add a Sylius Resource in sylius_resource.yaml

      sylius_resource:
          resources:
              ---
      
              vsorg.projects_categories:
                  driver: doctrine/orm
                  classes:
                      model:      App\Entity\ProjectCategory
                      interface:  Sylius\Component\Resource\Model\ResourceInterface
                      controller: App\Controller\AdminPanel\ProjectsCategoryController
                      repository: Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository
                      form:       App\Form\ProjectCategoryForm
    2. Add a Sylius Resource Route

      vsorg_projects_categories:
          resource: |
              alias: vsorg.projects_categories
              except: ['show']
              path: /projects/categories
              templates: "admin-panel/pages/ProjectsCategories"
          type: sylius.resource