Building Links and Routes
Addresses are generated from a route key, never written out, because every route is translated and the panel directory is not fixed.
Overview
A route key is a name; the address it produces depends on the language and on the installation. A literal path works in one language and answers 404 in the others. A panel link that hardcodes the admin directory breaks on any installation that renamed it.
Four static methods cover every case, all of them in coremio/classes/LinkGenerator.php.
Reference
public static function admin($route = '', $params = [], $lang = '', $wqs = []): string|bool;
public static function client($route = '', $params = [], $lang = '');
public static function wQS(null|string|bool $url, string|array $params = []): string;
public static function convert_to_link(string $arg): string;
wQS() when you need one.
? and & itself. The second argument must be an array in practice.
pages/12 into a full address. This is what makes a menu entry or an editor link portable. Results are memoized per request and language.
The Four Arguments
| Position | Argument | What it must be |
|---|---|---|
| 1 | $route | A key from the route table, not a path. An unknown key is not rejected, it is pasted into the address as is. |
| 2 | $params | Positional path segments. Each value fills the next (?) in the key's pattern, in order; leftovers are dropped silently. A non-array value is wrapped into a one element list. |
| 3 | $lang | '' uses the language being served. A code such as tr forces that language, and the literal none produces an address with no language segment at all. |
| 4 | $wqs | Query string as an associative array, applied only when it is non-empty. Present on admin() only. |
A Route Key Carries Its Segment Count
The router fills one (?) per parameter and stops. A key whose pattern has no placeholder ignores every parameter you pass. That is why the list and the detail address are two different keys rather than one key with arguments.
// coremio/locale/en/admin-routes.php - key => [address pattern, controller route]
return ['admin-routes' => [
'services' => ['services', 'services'],
'services-1' => ['services/(?)', 'services/(1)'],
'services-2' => ['services/(?)/(?)', 'services/(1)/(2)'],
]];
// LinkGenerator::admin("services", ["detail", 5]) -> /services both values dropped
// LinkGenerator::admin("services-1", ["detail"]) -> /services/detail
// LinkGenerator::admin("services-2", ["detail", 5]) -> /services/detail/5
What convert_to_link Accepts
| Form | Example | Resolves to |
|---|---|---|
| Site page key | home, contact, basket | That website route in the active language. |
| Client-area key | ca-invoices, ca-domains, ca-tickets | That client-area route. |
| Record reference | pages/12, products/48, kbase-page/7 | The record's detail address, looked up from its translated slug. |
| Category reference | category/25 | The catalogue, article, reference or knowledge base listing, chosen from the category's own type. |
| Product group | product-group/hosting | That group's catalogue page. |
| Anything else | whatever | The string #, so a broken reference appears as a dead link instead of an error. |
Example
// A list page: no parameters, no query string.
$list = LinkGenerator::admin("services");
// A detail page. The record id travels as a query string, so the fourth
// argument is used and the third stays empty.
$detail = LinkGenerator::admin("services-1", ["detail"], '', ['id' => $serviceId]);
// The same page opened on a tab.
$tab = LinkGenerator::admin("services-1", ["detail"], '', ['id' => $serviceId, 'tab' => "invoices"]);
// A website address forced into one language, for an alternate-language tag.
$signIn = LinkGenerator::client("sign-in", [], "tr");
// client() has no query string argument, so wrap it.
$paged = LinkGenerator::wQS(LinkGenerator::client("services"), ['page' => 2]);
// A stored reference from a menu row, resolved at render time.
$href = LinkGenerator::convert_to_link($row["page"]); // "pages/12" -> /about-us
// The second element of the entry is the controller route the address maps back to,
// which is why the key, not the path, is the thing code is allowed to know:
//
// 'services-1' => ['services/(?)', 'services/(1)']
// ^ written by ^ read by the router, which dispatches
// LinkGenerator services::page_detail()
class Services extends Controllers
{
public function page_detail(): void
{
$id = (int) Filter::init("GET/id", "rnumbers");
$this->addData("back", LinkGenerator::admin("services"));
}
}
Pitfalls
Writing LinkGenerator::admin("services-1", ["detail"], ['id' => 5]) puts the array where the language code belongs. Nothing is thrown. You get an Array to string conversion warning and the address /Array/{admin}/services/detail. The path segments in it still resolve, and the id you meant to send is nowhere. Pass '' for the language and keep the query string in position four.
The router returns the key itself when it is not in the table. A typo then produces a plausible looking address that answers 404, not an error at the point of the mistake. The same happens when a key exists but has fewer placeholders than the parameters you passed. The extra values vanish there.
The declared type of the second argument allows a string. The value is still handed to http_build_query(), which rejects anything that is not an array or object with a TypeError. Never concatenate a question mark by hand either: the generated address may already carry parameters, and you would produce a second one.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.