Nekonzistentí výstup $basePath (Presenter vs. template)

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

V případě, že člověk potřebuje zjistit basePath, tak zavolá:

V presenteru:

$this->context->httpRequest->url->basePath

V templatě:

{$basePath}

Zde nastane problém v podobě výstupu, který je rozdílný:

V presenteru:

"/"

V templatě:

""

Možná to místní Guru nebudou považovat za chybu (v podstatě to asi ani není), ale minimálně to docela mate. Člověk si stále musí dávat pozor, že v presenteru nesmí za basePath psát lomeno, ale v templatě naopak ano.

Tomáš Votruba
Moderator | 1114
+
0
-

V presenteru je lepší používat přímo konstantu WWW_DIR (APP_DIR aj.), o které víš, kam vede.
$basePath je určena pro detekci přesné adresy, kterou do té doby obvykle neznáš, např. pro přidání css souboru v šabloně.

To by ti mohlo vyřešit tento nesoulad.

nanuqcz
Člen | 822
+
0
-

Schmutzka: To je přece něco jiného, ne? WWW_DIR ti vrací cestu, co se týče filesystému na hostingu, $basePath zase cestu, která se týká URL (tyto dvě cesty jsou většinou různé).

Tomáš Votruba
Moderator | 1114
+
0
-

@nanuqcz: Chtěl jsem tím říct, že v presenteru je lepší provádět takové operace, které vyžadují pouze WWW_DIR. S věcmi vyžadujícími $basePath do šablony. Osobně mi to tak přijde čistší. Nechtěl jsem říct, že jsou stejné.

jtousek
Člen | 951
+
0
-

Osobně jakoukoli $basePath velmi nerad používám, protože aplikace mi stejně vždy běží na vlastní (sub)doméně. Pokud jde o localhost tak tam to jde nakonfigurovat taky. Obecně jsem s používáním $basePath měl vždy mnohem více problémů než užitku.

MartinitCZ
Člen | 580
+
0
-

Schmutzka: Ale v případě helperu, který cachuje obrázky a vrací jejich cestu (pro zobrazení na stránce), potřebuji basepath, nikoli WWW_DIR. V podstatě je to úplně stejné, jak si popsal to css.

nanuqcz: Přesně

LeonardoCA
Člen | 296
+
0
-

A co použít relativní cesty?

potřebuji vidět příklad, ale myslím si, že na webu se dá téměř vždy bez $basePath obejít.

jediný případ kdy je nutné používat $basePath je pro generování url tam kde není od čeho relativní cestu stavět, tj. do emailu nebo iframe
v tom případě vyznikne původní „problém“, ale pokud se zkombinuje relativní url s $basePath, tak to bude taky fungovat :-)

Nejlepší způsob jak otestovat, jestli se url vytváří správným způsobem, je vyzkoušet funkčnost projektu v těchto variantách:

Web musí na vlastní doméně i v podadresáři libovolné domény fungovat bez jediného zásahu do kódu a css. Jediný zásah potřebný zásah bývá do .htaccess pro nastavení RewriteBase.

Editoval LeonardoCA (21. 6. 2012 15:22)

Milo
Nette Core | 1283
+
0
-

Jak se bez $basePath obejdete? Představte si, že aplikace běží na http://host.name/user/app. Pokud v layoutu uvedu relativní cestu src="css/main.css" bez $basePath, tak jakmile prohlížeč načte url http://host.name/user/app/presenter/action/id/ tak se mi styl nenačte. S $basePath to bude fungovat vždy, protože se vygeneruje absolutní /user/app/css/main.css.

Milo
Nette Core | 1283
+
0
-

@martinit: Proč to je tak jak to je, nevím, ale můžeš si přeci udělat v BasePresenteru metodu

public function getBasePath()
{
	return rtrim($this->context->httpRequest->url->basePath, '/');
}

// a používat
$presenter->basePath;

Mě osobně by se ješte líbil template helper toUrl který by cestu ve filesystemu převedl na URL (pokud by to šlo).

Editoval Milo (21. 6. 2012 13:57)

LeonardoCA
Člen | 296
+
0
-

Milo napsal(a):

Jak se bez $basePath obejdete? Představte si, že aplikace běží na http://host.name/user/app. Pokud v layoutu uvedu relativní cestu src="css/main.css" bez $basePath, tak jakmile prohlížeč načte url http://host.name/user/app/presenter/action/id/ tak se mi styl nenačte. S $basePath to bude fungovat vždy, protože se vygeneruje absolutní /user/app/css/main.css.

src=„../css/main.css“

To je právě asi ta finta, kterou málokdo používá, že lze používat „..“ v cestách na obrázky, css, js. basePath zná prohlížeč …

A to nejde použít jen tam kde jsou potřeba celé url včetně názvu domény.

Editováno: omlouvám se za mystifikaci, tady musí být použito basepath, myslel jsem něco jíného. Až zjistím jak se tu přeškrkavá, tak neplatící přeškrnu..

Editoval LeonardoCA (21. 6. 2012 15:23)

MartinitCZ
Člen | 580
+
0
-

Milo napsal(a):

@martinit: Proč to je tak jak to je, nevím, ale můžeš si přeci udělat v BasePresenteru metodu

public function getBasePath()
{
	return rtrim($this->context->httpRequest->url->basePath, '/');
}

// a používat
$presenter->basePath;

Mě osobně by se ješte líbil template helper toUrl který by cestu ve filesystemu převedl na URL (pokud by to šlo).

Právě proto jsem to nahlásil a rád bych to nějak kloudně vyřešil, nejlépu úpravou.
Jak jsi tu sám zmínil $basePath je potřeba, tak by bylo dobré, kdyby byl výstup v obou případech stejný!

Já používám toto, jelikož do případnéch helperů předávám kontext, ale presenter ne.

$this->context->parameters["basePath"] = preg_replace("#https?://[^/]+#A", "", rtrim($this->context->httpRequest->url->baseUrl, "/"));

Vytvořit helper toUrl je to nejmenší, ale nebude to přímo v Nette, což ti asi nevyhovuje.

22
Člen | 1478
+
0
-

..není nic jednoduššího, než poslat pull request

MartinitCZ
Člen | 580
+
0
-

22: Prosimtě, udělaš to ty? Případně někdo jiný? Github nemám, takže asi ani pull request neuměl udělat. ;)

Díky.

KingKoca
Člen | 25
+
0
-

Taky nevim, jaky je duvod odstraneni lomitkana konci basePath (v sablone). Trpi tim take baseUrl. Pokud to opravdu nema zadny zavazny duvod, je to nesmysl..

Editoval KingKoca (8. 7. 2012 16:12)

Aurielle
Člen | 1281
+
0
-

@KingKoca: až na to, že zmíněné chování je ve frameworku již hodně dlouho (víc jak rok? Každopádně určitě od verze 0.9.x) a dosud si na něj nikdo nestěžoval. Dříve se používala proměnná $baseUri a právě z toho důvodu, že obsahovala lomítko, se přidala i $basePath. A z $baseUri se pak stala tuším absolutní adresa.

Filip Procházka
Moderator | 4668
+
0
-

Funguje to správně. Při práci v šabloně jsou jiné požadavky, než při práci v presenteru/modelu.

Tomáš Votruba
Moderator | 1114
+
0
-

HosipLan napsal(a):

Funguje to správně. Při práci v šabloně jsou jiné požadavky, než při práci v presenteru/modelu.

Konkrétní příklad by lépe podpořil/osvětlil situaci, třeba je to logické. Dáš?

KingKoca
Člen | 25
+
0
-

@gmvasek: to je to same jako veta „je tam chyba hodne dlouho.“ Treba o Windows se to rika casto :) Ikdyz jsou zde historicke duvody zrejme, moc me nezajimaji. Treba ja zacal pouzivat Nette az od verze 2.0 a tato nekonzistentnost me nemile prekvapila a sebrala mi nekolik minut diky tomu, ze jsem musel najit a opravit chybu – tedy opravit standardni chovani.

@HosipLan: A to ja mam zas naopak pozadavky na $baseUrl a $basePath stejne :) Kdyz v sablone pisu odkaz, zdroj (href=„..“, src=„..“), musim spojovat $basePath a cestu lomitkem. Kdyz v presenteru presmerovavam ($this->redirectUrl(„…“)), proc cesta najednou lomitko obsahuje? Kdyz v sablone pouzivam tag <base href>, musim tam opet dat rucne lomitko, posledni cast url bez lomitka neni soucasti „base.“ Opet zde musim myslet na to, ze v presenteru je to jinak nez v sablone.

Kazdopadne nedoufam v opravu, protoze by to rozbilo spoustu jiz hotovych projektu a mozna nez zneprijemnit zivot hromade vyvojaru spravujici stovky aplikaci, tak je lepsi zneprijemnit zivot tem, kteri sve aplikace teprve zacinaji vyvijet. Mozna kdyby to bylo v dokumentaci, mohl bych o tom vedet od zacatku.

Diky :)

Aurielle
Člen | 1281
+
0
-

Můžu se zeptat, proč v presenteru přesměrováváš přes redirectUrl?

Honza Marek
Člen | 1664
+
0
-

Schmutzka napsal(a):

V presenteru je lepší používat přímo konstantu WWW_DIR (APP_DIR aj.)

Pokud někde použiju takovou konstantu, můžu se rozloučit s tím, že příslušný kód půjde otestovat. V presenteru dejme tomu, ale v modelu už bych si na to netroufnul.

KingKoca
Člen | 25
+
0
-

gmvasek napsal(a):

Můžu se zeptat, proč v presenteru přesměrováváš přes redirectUrl?

Protoze nepouzivam klasickou strukturu aplikace <module>/<presenter>/<action>. Prubeh aplikace je zavisly na jednotlivych castech url, pouzivam vlastni Router. Nazev presenteru je prirazen k url v databazi a jeden presenter muze spadat pod vice url adres (s ruznymi „params“). Proto neni mozne (jednoduche?) vykonstruovat jednoznacnou url adresu podle kodu v metode constructUrl() v routeru. Proto $destination vytvarim jinak (casto se jedna o cast soucasne url, kterou mam jiz v promenne), preskocim spoustu zbytecneho (pro mou aplikaci) kodu a volam $this->redirectUrl() funkci, kterou $this->redirect() stejne na konci vola taky.

To ale vubec nijak nesouvisi tematem. Proc je $basePath v presenteru jina nez v sablone? Mohl bych se spise ptat „je to bug nebo feature?“ Ale to bych dostal odpoved feature a nikdo by to dal neresil :) Chtel bych, aby se nad tim nekdo zamyslel..

Filip Procházka
Moderator | 4668
+
0
-

@KingKoca: Bullshit, s Nette routami uděláš všechno, používáš je špatně.

Je to feature.

Tomáš Votruba
Moderator | 1114
+
0
-

@KingKoca: Zkus napsat, co potřebuješ, v čem ti přijde Nette router nedostatečný a jak to nyní řešíš (real situation).

KingKoca
Člen | 25
+
0
-

Probuh, jak toto vsecko souvisi s tematem? Muze nekdo kvalifikovany odpovedet ohledne lomitka na konci $basePath a $baseUrl v presenteru / sablone? Proc jsou jejich hodnoty jine?

P.S.: ocenuji vsak, ze se snazite pomoci. Protoze jsem v Nette novacek, je dost pravdepodobne, ze neco delam spatne.
Zjednodusene – mam teoreticky nekonecnou url a v jednom kroku se zpracovava jedna cast cesty (casti se oddeluji lomitkem). Podle casti se provede nejaka akce a prubeh te akce zavisi na nasledujici casti cesty. A takhle dal, dokud modul nezpracuje tolik casti url, kolik modul umi.
Napr adresa galerie/chata/2010/silvestr vede na jednu akci. na stejnou, jako adresy: galerie, galerie/chata, galerie/chata/2010. Nebo url adresy dalsi-galerie/subgalerie/subsubgalerie/ctvrta-uroven-galerie. Vsecky vedou na stejnou akci. Vystupem teto akce je seznam obrazku podle url. Url jednotlivych galerii jsou ulozeny v databazi ve stromove strukture.

Na teoreticky nekonecnou url jsem na techto forech nasel jedinou odpoved: vlastni router.
Generovani url adresy podle presenteru / akce nelze, protoze jak je na prikladu videt, jedna akce muze zpracovat vice url adres. Takze generovani url probiha jinak a nakonec se zavola redirectUrl. Je to opravdu podobne, jako s metodou redirect. Ta generuje url na zaklade nejakych pravidel a nakonec zavola redirectUrl. Ja generuji url take na zaklade nejakych pravidel a nakonec zavolam redirectUrl, ale potrebuji k tomu mene kodu, je to jednodussi nez pres metodu redirect.

A protoze nette je zname tim, ze nikomu nic nenuti, nenuti me pouzivat metodu redirect, muzu si url adresu vygenerovat jak budu chtit. Potom ale ke spravnemu presmerovani potrebuju $basePath, resp. $baseUrl. S tim by nebyl problem, kdybych necekal, ze na konci lomitko neni, jak je znamo z pouzivani v sablone. A to je cele, o co tu jde.

Editoval KingKoca (10. 7. 2012 21:10)

Filip Procházka
Moderator | 4668
+
0
-

Ano, používáš to špatně :) Dobře ti radím neobcházej to, systémové řešení je vždy lepší.

Já mám routu napsanou takto a v node můžu mít cokoliv. A pořád využívám plnou sílu Nette.

class NodeRouter extends Nette\Application\Routers\RouteList
{
	/**
	 * @var ContentRepository\PathMap
	 */
	private $pathMap;

	/**
	 * @param ContentRepository\PathMap $pathMap
	 */
	public function __construct(ContentRepository\PathMap $pathMap)
	{
		$this->pathMap = $pathMap;

		$this[] = new Route('[<language [a-z]{2,3}>/][<node .*>][.<format>]?action=<action>', array(
			'presenter' => 'Node',
			'action' => 'default',
			'language' => array(
				Route::VALUE => NULL,
				Route::FILTER_IN => function ($lang) {
					return in_array($lang, array('cs', 'en')) ? $lang : NULL;
				},
			),
			'node' => array(
				Route::VALUE => NULL,
				Route::FILTER_IN => callback($this, 'filterInNode'),
				Route::FILTER_OUT => callback($this, 'filterOutNode'),
			),
			'format' => NULL,
		));
	}

	/**
	 * @internal
	 * @param string $nodePath
	 * @return int|NULL
	 */
	public function filterInNode($nodePath)
	{
		if (is_numeric($nodePath)) {
			return $nodePath;
		}

		return $this->pathMap->pathId(trim($nodePath, '/'));
	}

	/**
	 * @internal
	 * @param string $node
	 * @return null|string
	 */
	public function filterOutNode($node)
	{
		if (is_object($node)) {
			/** @var \Kdyby\CMS\Sitemap\AbstractNode $node */
			return $node->getPath() ?: NULL;

		} elseif (!is_numeric($node)) {
			return $node ?: NULL;
		}

		return $this->pathMap->absolutePath($node) ?: NULL;
	}

}

Něco dalšího ke čtení

Editoval HosipLan (10. 7. 2012 23:15)

Milo
Nette Core | 1283
+
0
-

Jsem radši že v šabloně je $basePath bez lomítka. Lomítko se snáz přidává, hůř odebírá. Navíc to vypadá přehledněji při psaní cesty:

{$basePath}/images/{$img}
vs.
{$basePath}images/{$img}
KingKoca
Člen | 25
+
0
-

Diky Ty, jehož jméno se nám podaří pokaždé zkomolit :) Zkusim na tom zapracovat a snad se mi to povede.

A nakonec jiz tradicni (recnicka) otazka: Proc ma $basePath v sablone a presenteru jine hodnoty? :)

Filip Procházka
Moderator | 4668
+
0
-

Kdyby se to v šabloně jmenovalo $relativePathForStaticFiles, tak by tě to ani nenapadlo :)

woytam
Člen | 14
+
0
-

Jednou z možností je použít v HEAD sekci HTML příkaz pro nastavení základní URL:

<base href="{$baseUrl}/" />

Více na http://www.w3schools.com/…tag_base.asp

bene
Člen | 82
+
0
-

Milión keců kolem, ale nikdo nedokázal relevantně odpovědět, jaký je rozdíl práce s basePath v presenteru a šabloně a tudíž oddůvodnit jejich nekonzistentnost.

Mě osobně to také smysl nedává mít basePath v presenteru jinou než v šabloně.

elendir
Člen | 31
+
0
-

bene napsal(a):

Milión keců kolem, ale nikdo nedokázal relevantně odpovědět, jaký je rozdíl práce s basePath v presenteru a šabloně a tudíž oddůvodnit jejich nekonzistentnost.

+1

Milo
Nette Core | 1283
+
0
-

Odhaduji, že to je historická záležitost. Sjednotit to je ale obrovský BC break jak pro vlastní aplikační kód, tak pro spoustu doplňků.

Není přeci problém, otevřít si issue na GitHubu, popsat tam proč je to problém a nejlépe navrhnout co nejméně bolestivé řešení.

MartinitCZ
Člen | 580
+
0
-

Lepší je si na to zvyknout. Pokud @httpRequest předávám do nějaké komponenty, doplnku … tak na basepath ihned hodim rtrim. Viz. Milo

David Grudl
Nette Core | 8228
+
0
-

Issue bez návrhu, jak věc bezbolestně řešit, prosím vůbec neotevírejte.

h4kuna
Backer | 740
+
0
-

Co zrušit $basePath a nahradit je makry? n:baseSrc="" a n:baseHref="" která by se zkompilovalo a vrátilo celou cestu že by basePath bylo napevno ve zkompilované šabloně?

n:baseSrc="css/screen.css" -> src="/sandbox/css/screen.css"
n:baseSrc="/css/screen.css" -> src="/sandbox/css/screen.css"
n:baseHref="//css/screen.css" -> href="http://localhost/sandbox/css/screen.css"

A udělat to tak aby bylo jedno když člověk lomítko napíše či nikoliv.

<?php

use Nette\Object;
use Nette\Http\Request;

/**
 * Pathnizer
 * add slash on start path and on the end remove slash
 * @example
 * $pathnizer->buildFs('/css/', '/screen.css'); // /css/screen.css
 * $pathnizer->buildFs('/css', 'screen.css'); // /css/screen.css
 * $pathnizer->buildFs('css', 'screen.css'); // /css/screen.css
 * $pathnizer->buildFs('css/screen.css'); // /css/screen.css
 * $pathnizer->buildFs('../css/screen.css'); // ../css/screen.css
 * $pathnizer->buildFs('./css/screen.css'); // ./css/screen.css
 * $pathnizer->buildFs('C:/css/screen.css'); // C:/css/screen.css
 * $pathnizer->buildFs('css/screen'); // /css/screen
 * $pathnizer->buildFs('css/screen/'); // /css/screen
 *
 * // add basePath from Request
 * $pathnizer->buildUrl('/css/screen.css'); // /sandbox/www/css/screen.css
 * $pathnizer->buildUrl('css/screen.css'); // /sandbox/www/css/screen.css
 * $pathnizer->buildUrl('//css/screen.css'); // http://localhost/sandbox/www/css/screen.css
 *
 * @author h4kuna
 */
class Pathnizer extends Object {

    /**
     * @var bool
     */
    private static $used;

    /** @var Nette\Http\UrlScript */
    private $url;

    public function __construct(Request $request) {
        $this->url = $request->getUrl();
    }

    /**
     * @params string
     * @return string
     */
    public function buildUrl(/* ... */) {
        $url = preg_match('~^//~', func_get_arg(0)) ? $this->url->getBaseUrl() : $this->url->getBasePath();

        return $url . ltrim($this->joinPath('/', func_get_args()), '\/');
    }

    /**
     * @params string
     */
    public function buildFs(/* ... */) {
        return $this->joinPath(DIRECTORY_SEPARATOR, func_get_args());
    }

    /**
     *
     * @param string $delimiter
     * @param array $path
     * @return string
     */
    private function joinPath($delimiter, array $path) {
        $outPath = NULL;
        self::$used = FALSE;

        foreach ($path as $p) {
            if (self::isException($p)) {
                $outPath = rtrim($p, '\/');
            } else {
                $outPath .= $delimiter . trim($p, '\/');
            }
        }
        return $outPath;
    }

    /**
     * @param string $path
     * @return boolean
     */
    private static function isException($path) {
        if (self::$used) {
            return FALSE;
        }
        self::$used = TRUE;
        // windows or relative
        return preg_match('~^([A-Z]:|\.{1,2})~', $path);
    }

}
?>