komponenty – subpattern name is too long (maximum 32 characters)

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Zax
Člen | 370
+
0
-

Zdravím,

tvořím web složený z komponent (přišlo mi to jako dobrý nápad…) a mám problém s routováním. Hluboko v komponentovém stromě mám stránkovač, kterému chci nastavit hezké url.

Zkusil jsem toto:

$router[] = new Routers\Route('administrace/skupiny[/<rolesPanel-rolesList-paginator-page>]', 'Web:Back:Users:roles');

Nette vyhodí výjimku:

Nette\Utils\RegexpException
preg_match(): Compilation failed: subpattern name is too long (maximum 32 characters) at offset 69 in pattern: #administrace/skupiny(?:/(?P<rolesPanel___rolesList___paginator___page>(?U)[^/]+))?/?\z#Aiu

Jde to nějak chytře obejít?

Persistentní parametry (nebo jakoukoliv jinou režii navíc) v presenterech bych rád vyloučil, pokud možno.

Děkuji.

Editoval Zax (12. 5. 2014 18:24)

Jan Tvrdík
Nette guru | 2595
+
0
-

Ano, jde.

$router[] = new Routers\Route('administrace/skupiny[/<paginator-page>]', [
    'presenter' => 'Web:Back:Users',
    'action' => 'roles',
    NULL => [
        Routers\Route::FILTER_IN => function ($params) {
            if (isset($params['paginator-page']) {
                $params['rolesPanel-rolesList-paginator-page'] = $params[''];
                unset($params['paginator-page']);
            }
            return $params;
        },
        Routers\Route::FILTER_OUT => function ($params) {
             if (isset($params['rolesPanel-rolesList-paginator-page']) {
                $params['paginator-page'] = $params['rolesPanel-rolesList-paginator-page'];
                unset($params['paginator-page']);
            }
            return $params;
        },
    ]
]);
Zax
Člen | 370
+
0
-

Funguje (s drobnými úpravami, ale myšlenku jsi vyjádřil). Díky moc!

Next step: rozšířit si router o nějaké jednoduché API, co tohle bude řešit za mě :-)

Zax
Člen | 370
+
0
-

Přidal jsem si do továrny tuhle metodu

    protected function createAliases($aliases) {
        if(func_num_args() > 1)
            $aliases = array($aliases => func_get_arg(1));
        return array(
            Routers\Route::FILTER_IN => function($params) use ($aliases) {
                foreach($aliases as $alias => $fullName) {
                    if(isset($params[$alias])) {
                        $params[$fullName] = $params[$alias];
                        unset($params[$alias]);
                    }
                }
                return $params;
            },
            Routers\Route::FILTER_OUT => function($params) use ($aliases) {
                foreach($aliases as $alias => $fullName) {
                    if(isset($params[$fullName])) {
                        $params[$alias] = $params[$fullName];
                        unset($params[$fullName]);
                    }
                }
                return $params;
            }
        );
    }

a volám takto:

    $router[] = new Routers\Route('administrace/skupiny[/<page>]', array(
        'presenter' => 'Web:Back:Users',
        'action' => 'roles',
        NULL => $this->createAliases(array('page'=> 'rolesPanel-rolesList-paginator-page'))
        // nebo createAliases('page', 'rolesPanel-rolesList-paginator-page') pro lenochy
    ));

Třeba se to bude někomu hodit ;-)

enumag
Člen | 2118
+
0
-

Spíše by bylo lepší upravit to přímo v nette aby router dlouhé parametry nepoužíval jako subpattern name v regexpu ale třeba je zahashoval pomocí md5 nebo vytvářel aliasy automaticky. Tohle chování je imho bug a neměly by být potřeba takové okliky.

Editoval enumag (12. 5. 2014 23:33)

Majkl578
Moderator | 1364
+
0
-

enumag napsal(a):

Souhlas. Reportuj na Githubu, ideálně přímo pull request.

enumag
Člen | 2118
+
0
-

@Majkl578: Balík nette/routing je zatím testovací takže správné místo pro report je nette/application že?

https://github.com/…ation/pull/7

Zax
Člen | 370
+
0
-

Bylo by taky fajn, kdyby router uměl nativně překládat názvy komponent tvořené multiplierem a „do“ parametry.

Trochu jsem si svůj script ještě upravil, takže můžu tvořit třeba takovéto routy:

        $router[] = new Routers\Route('admin/skupiny[/zobrazit-opravneni-<o>][/strana-<pg>][/skupina-<r>][/pravidla-skupiny-<p>[/strana-<pg2>][/pravidlo-<rr>]][/akce-<do>[/zobrazit-<sp>]]', array(
            'presenter' => 'Web:Back:Users',
            'action' => 'roles',
            NULL => $this->createAliases(
                    array(
                        'pg' => 'rolesPanel-rolesList-paginator-page',
                        'p' => 'rolesPanel-rolesList-editPermissions',
                        'r' => 'rolesPanel-rolesList-id',
                        'o' => 'rolesPanel-rolesList-showPermissions',
                        'pg2' => 'rolesPanel-rolesList-permissions-<p>-paginator-page',
                        'rr' => 'rolesPanel-rolesList-permissions-<p>-id',
                        'sp' => 'rolesPanel-rolesList-show'
                    ),
                    array(
                        'vytvorit-skupinu' => 'rolesPanel-rolesList-add',
                        'upravit-skupinu' => 'rolesPanel-rolesList-edit',
                        'odstranit-skupinu' => 'rolesPanel-rolesList-delete',
                        'vytvorit-pravidlo' => 'rolesPanel-rolesList-permissions-<p>-add',
                        'upravit-pravidlo' => 'rolesPanel-rolesList-permissions-<p>-edit',
                        'odstranit-pravidlo' => 'rolesPanel-rolesList-permissions-<p>-delete',
                        'prepnout-opravneni' => 'rolesPanel-rolesList-toggleShowPermissions',
                        'prepnout-pravidla' => 'rolesPanel-rolesList-editPermissions'
                    )
        )));

První pole jsou aliasy pro parametry, druhé pole jsou aliasy pro „do“ a všimněte si, že se odkazuji na parametr <p>, který obsahuje IDčko, které se používá v multiplieru. Výsledná URL pak vypadá třeba takto:

admin/skupiny/strana-3/pravidla-skupiny-30/strana-2/pravidlo-50/akce-upravit-pravidlo

místo zběsilosti typu

rolesPanel-rolesList-paginator-page=3&rolesPanel-rolesList-editPermissions=30&rolesPanel-rolesList-permissions-30-paginator-page=2&rolesPanel-rolesList-permissions-30-id=50&do=rolesPanel-rolesList-permissions-30-edit

Quick n dirty solution (určitě to jde napsat líp..)

    protected function findTokens($name) {
        return Nette\Utils\Strings::match($name, '~(\<[a-z-]\>)+~i');
    }

    protected function createFullName($tokens, $params, $aliases, $fullName) {
        foreach($tokens as $token) {
            $token2 = substr($token, 1, -1);
            if(isset($params[$token2])) {
                $fullName = str_replace($token, $params[$token2], $fullName);
            } else if(isset($aliases[$token2]) && isset($params[$aliases[$token2]])) {
                $fullName = str_replace($token, $params[$aliases[$token2]], $fullName);
            }
        }
        return $fullName;
    }

    protected function createAliases($aliases, $doAliases = array()) {

        $that = $this;
        return array(
            Routers\Route::FILTER_IN => function($params) use ($aliases, $doAliases, $that) {
                if(isset($params['do'])) {
                    foreach($doAliases as $doAlias => $doFullName) {
                        $doTokens = $that->findTokens($doFullName);
                        if($doTokens) {
                            $doFullName = $that->createFullName($doTokens, $params, $aliases, $doFullName);
                        }
                        if($params['do'] == $doAlias)
                            $params['do'] = $doFullName;
                    }
                }
                foreach($aliases as $alias => $fullName) {
                    if(isset($params[$alias])) {
                        $tokens = $that->findTokens($fullName);
                        if($tokens) {
                            $fullName = $that->createFullName($tokens, $params, $aliases, $fullName);
                        }
                        $params[$fullName] = $params[$alias];
                        unset($params[$alias]);
                    }
                }

                return $params;
            },
            Routers\Route::FILTER_OUT => function($params) use ($aliases, $doAliases, $that) {
                if(isset($params['do'])) {
                    foreach($doAliases as $doAlias => $doFullName) {
                        $doTokens = $that->findTokens($doFullName);
                        if($doTokens) {
                            $doFullName = $that->createFullName($doTokens, $params, $aliases, $doFullName);
                        }
                        if($params['do'] == $doFullName)
                            $params['do'] = $doAlias;
                    }
                }
                foreach($aliases as $alias => $fullName) {
                    $tokens = $that->findTokens($fullName);
                    if($tokens) {
                        $fullName = $that->createFullName($tokens, $params, $aliases, $fullName);
                    }
                    if(isset($params[$fullName])) {
                        $params[$alias] = $params[$fullName];
                        unset($params[$fullName]);
                    }
                }

                return $params;
            }
        );
    }

Editoval Zax (13. 5. 2014 18:19)