Symfony 4 routing from DB

2019-08-20 03:10发布

问题:

I'm creating a simple CMS with Symfony 4. Where it it's possible to create pages. When creating a page a Doctrine EvenSubscriber is being called. This subscriber creates an PageRoute.

<?php
#src/Doctrine/EvenListener 

/**
 * PageRouteSubscriber
 */

namespace App\Doctrine\EventListener;


use App\Controller\PageController;
use App\Entity\Page;
use App\Entity\Route;
use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;

/**
 * Class PageRouteSubscriber
 * @package App\Doctrine\EventListener
 */
class PageRouteSubscriber implements EventSubscriber {
    /**
     * Returns an array of events this subscriber wants to listen to.
     * @return array
     */
    public function getSubscribedEvents() {
        return [
            'postPersist',
            'postUpdate',
        ];
    }

    public function postPersist(LifecycleEventArgs $args){
        $this->index($args);
    }

    public function postUpdate(LifecycleEventArgs $args){
        $this->index($args);
    }

    public function index(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();
        if($entity instanceof Page){
            $route = new Route();
            $route->setController(PageController::class);
            $route->setRouteContentId(get_class($entity) . '#' . $entity->getId());
            $route->setLocale($entity->getLocale());
            $route->setSlug($entity->getSlug());

            $entityManager = $args->getObjectManager();
            $entityManager->persist($route);
            $entityManager->flush();
        }
    }
}

So when the page is created there also is an route created. See images for database page and route examples.

Page

Route

I don't know this is the best way to store routes and pages in DB. Because they have both the same slug. I was thinking, only set slugs in route table. And per entity checking the unique slug based on all slugs in the route table (if this is possible?).

For routing: I don't now how to grep the routes and use them with Symfony Route Collection. Also is it possible to cache te routes just like Symfony does and created on big file called: srcDevDebugProjectContainerUrlGenerator

When i've got routes working i could create the frontend menu. Where a page menu item is coupled to an page. That page has an route. With this route the menu url could be created (is the the right way of thinking?).

回答1:

I think you should avoid storing class names in your database. If you change your namespace then they worn't work anymore. And I don't see why you need a route table?

You can just do it similar to how most CMSs are doing it: just use slug from your page entity, and let symfony param converter handle the rest, or load it manually.

/**
 * @Route("/page/{slug}")
 * @ParamConverter("page")
 */
public function showAction(Page $page)
{
}

You can get locale trough urls like this.

for your menu, you can just create a "menu" entity and add a relation to your page entity or hardcore a slug/url field in it.



回答2:

Was solving something similiar in past in reality you need to take few steps. My need was to have routes without prefix (so no /article/{slug}, but just {slug} and I have quite large number of them so DB looks more appropriate.

  • Storing routes - I have routes table where I was storing url to match, target controller and parameters to pass (I couldn't include ID in route)
  • In your case - Article will have route_id as one of its properties. This will as well solve generating of routes, because you will fetch them from database via association
  • When creating new route I made check if route with requested name exists, if so I have added incrementing number to the end.
  • Then I have one general route at the end of my normal routes to match everything /*, I hooked to the kernel events and if this route was matched AND I found mu current request in my database - I have modified request to point to my database record controller

This can be easily extended for example to track history of changes (Let's say you have Article First Article (slug: /first-article), then you rename it to Second Article (slug changed: /second-article) here you can easily track this change and make proper redirect.



回答3:

You don't need to store routes in database, have a look at CMF router components, you can use the dynamic router to route any cms pages based on the slug (https://symfony.com/doc/master/cmf/bundles/routing/dynamic.html), then use chain router (https://symfony.com/doc/master/cmf/components/routing/chain.html) to use it in conjunction with the standard SF router.