statamic / cms

The core Laravel CMS Composer package
https://statamic.com
Other
4.03k stars 530 forks source link

Strange urls: nested entries include {mount} several times #10838

Closed alexveklenko closed 1 month ago

alexveklenko commented 1 month ago

Bug description

Problem: {parent_uri} appends {mount} at every point in URI hierarchy, so with depth > 1 it starts to add up, e.g.: given permalink structure /{mount}/{parent_uri}/{slug} it behaves like this: 1st level - /top/lvl1 (parent_uri = '') 2nd level - /top/top/lvl1/lvl2 (parent_uri = '/top/lvl1') 3rd level - /top/top/top/lvl1/lvl2/lvl3 (parent_uri = '/top/top/lvl1/lvl2')

How to reproduce

Orderable collection's route structure is '/{mount}/{parent_uri}/{slug}' Create nested entries and check uri

Logs

No response

Environment

Environment
Application Name: Westart
Laravel Version: 11.20.0
PHP Version: 8.2.12
Composer Version: 2.7.9
Environment: local
Debug Mode: ENABLED
URL: westart-statamic.local
Maintenance Mode: OFF
Timezone: UTC
Locale: en

Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED

Drivers
Cache: file
Database: mysql
Logs: stack / single
Mail: smtp
Queue: sync
Session: file

Statamic
Addons: 4
Sites: 1
Stache Watcher: Enabled
Static Caching: Disabled
Version: 5.26.0 PRO

Statamic Addons
alt-design/alt-redirect: 1.3.2
statamic/collaboration: 1.0.0
statamic/eloquent-driver: 4.12.3
statamic/seo-pro: 6.0.3

Statamic Eloquent Driver
Asset Containers: eloquent
Assets: eloquent
Blueprints: file
Collection Trees: eloquent
Collections: file
Entries: eloquent
Forms: eloquent
Global Sets: eloquent
Global Variables: eloquent
Navigation Trees: eloquent
Navigations: eloquent
Revisions: eloquent
Sites: file
Taxonomies: file
Terms: eloquent
Tokens: eloquent

Installation

Fresh statamic/statamic site via CLI

Additional details

Possible solution: src/Routing/UrlBuilder.php

public function build($route)
    {
        // Routes can be defined as a string for just the route URL,
        // or they can be an array with a route for each locale.
        $route = (is_array($route)) ? $route[$this->content->locale()] : $route;

        $route = $this->convertToAntlers($route);

        // keep {mount} only for the top level entries
        $routeData = $this->routeData();
        if (isset($routeData['depth']) && $routeData['depth'] > 1) {
            $routeData['mount'] = '/';
        }

        $url = Antlers::parse($route, $routeData);

        // Slugify it because we're dealing with URLs after all.
        $url = $this->slugify($url);

        // If provided variables had no matching value, we would end up with
        // blank spaces in the URL, possibly resulting in double slashes.
        // Tidying up the URL will de-duplicate those extra slashes.
        $url = URL::tidy($url);

        $url = rtrim($url, '/');

        return Str::ensureLeft($url, '/');
    }
jasonvarga commented 1 month ago

Only include {mount} at the top level.

route: '{{ depth == 1 ?= mount }}/{{ parent_uri }}/{{ slug }}'

or

route: '{{ if depth == 1 }}{{ mount }}/{{ /if }}{{ parent_uri }}/{{ slug }}'

https://statamic.dev/collections#example-routes

alexveklenko commented 1 month ago

Hi @jasonvarga, thank you very much!