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)