Search code examples
content-management-systemsymfonysymfony-cmfsymfony-routing

How can I add simple CMS functionality to existing Symfony application


I have an existing web application accessing a MySQL database. I'm porting this application to Symfony. The new application has to use the old database, as we cannot port the whole application at once, i.e. the old and the new application are accessing the same database and the applications are running simultaneously.

The old application had a simple CMS functionality which has to be ported:

There is a table pagewhich represents a page tree. Every page has a slug field. The URL path consists of those slugs representing the path identifying the page node, e.g. "/[parent-slug]/[child-slug]".

The page table also contains a content field. As I already mentioned, the CMS functionality is very simple, so the content is just rendered as page content inside a page layout. The page entry also specifies the page layout / template.

My problem is that I don't know how to set up the routing. In a normal Symfony application I'd know the URL patterns before, but in this case they are dynamic. Also routes cannot be cached, because they could be changed any time by the user. I wonder if I have to drop Symfony's routing completely and implement something on my own. But how?

Now I found Symfony CMF which tells a lot about the framework VS CMS routing conflict. So first, I thought this would be the right way. However the tutorials aim at building an entirely new application based on PHPRC. I wasn't able to derive the tutorial's concepts to my use case.


Solution

  • since you run several URL rules on one symfony application, you will need to work with url prefixes. Either your cms should work with a prefix /cms/parent-slug/child-slug or all other controllers. Otherwise you are not able to differ which controller is meant when a dynamic request arrives.

    You can try a workaround with a KernelControllerListener. He will catch up every request and then check if a cms page is requested. On the basis of the request you can set controller and action by yourself. Concept:

    Create only one route with "/". Abandon oll other rules. Then create a Listener like this:

    <?php
    
    namespace AppBundle\Listener;
    use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
    
    /**
     * Class KernelControllerListener
     * @package ApiBundle\Listener
     */
    class KernelControllerListener
    {
        /**
         * @var CmsRepository
         */
        private $requestParser;
    
        /**
         * KernelControllerListener constructor.
         * @param CmsRepository $CmsRepository
         */
        public function __construct(CmsRepository $CmsRepository)
        {
            $this->CmsRepository = $CmsRepository;
        }
    
        /**
         * @param FilterControllerEvent $event
         */
        public function onKernelController(FilterControllerEvent $event){
            $request = $event->getRequest();
            //should be /parent-slug/children/slug or any other path
            $path = $request->getPathInfo();
    
            if($this->CmsRepository->getCmsControllerIfMatch($path)){
                //cms repository search in db for page with this path, otherwise return false
                $event->setController([AppBundle\CmsController::class, 'cmsAction']);
                return;
            }
    
            //repeat if clause for any other application part
        }
    }
    

    in services.yml:

    app.controller_listener:
      class: AppBundle\Listener\KernelControllerListener
      arguments:
        - "@app.cms_repository"
      tags:
        - { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
    

    Edit: catch all routes, see https://www.jverdeyen.be/symfony2/symfony-catch-all-route/