Název článku/postu v URL z databáze
- Checkpoint
- Člen | 34
Zdravím,
dělám aplikaci v Nette a potřeboval bych, aby v URL
localhost/homepage/detail/0 se místo ID zobrazil název
(title) postu/článku z databáze.
Projížděl jsem fórum i dokumentaci a vyčetl jsem, že se to dělá pomocí
FILTER_IN a FILTER_OUT.
Zkoušel jsem všechno, ale zatím neúspěšně.
V modelu mám vytvořenou statickou funkci, která má vzít název podle id.
<?php
namespace Model;
class AdRepository extends Repository {
public function getAll() {
return $this->database->table('ad');
}
public function getDetail($id) {
return $this->database->table('ad')->where('idAd = ?', $id);
}
public function getIdByUrl() {
}
public static function getTitleById() {
//zde se má vracet title podle idAd
}
}
?>
- Pavel Macháň
- Člen | 282
Checkpoint napsal(a):
Zdravím,
dělám aplikaci v Nette a potřeboval bych, aby v URL localhost/homepage/detail/0 se místo ID zobrazil název (title) postu/článku z databáze.
Projížděl jsem fórum i dokumentaci a vyčetl jsem, že se to dělá pomocí FILTER_IN a FILTER_OUT.
Zkoušel jsem všechno, ale zatím neúspěšně.V modelu mám vytvořenou statickou funkci, která má vzít název podle id.
<?php namespace Model; class AdRepository extends Repository { public function getAll() { return $this->database->table('ad'); } public function getDetail($id) { return $this->database->table('ad')->where('idAd = ?', $id); } public function getIdByUrl() { } public static function getTitleById() { //zde se má vracet title podle idAd } } ?>
Statiku z toho vynech. Do router factory si injectni pomocí konstruktoru
tento model.
A jak funguje FILTER_IN | FILTER_OUT je dobře vysvětleno v Plannete
- Checkpoint
- Člen | 34
Model AdRepository.php
<?php
namespace Model;
class AdRepository extends Repository {
public function getAll() {
return $this->database->table('ad');
}
public function getDetail($id) {
return $this->database->table('ad')->where('idAd = ?', $id);
}
public function getIdByUrl() {
}
public function getTitleById($id) {
return $this->database->query('SELECT title FROM ad WHERE idAd = ?', $id); //získání title
}
}
?>
RouterFactory.php
<?php
namespace App;
use Nette,
Nette\Application\Routers\RouteList,
Nette\Application\Routers\Route,
Nette\Application\Routers\SimpleRouter,
Nette\Utils\Strings;
/**
* Router factory.
*/
class RouterFactory
{
public $adRepository;
public function inject(Model\AdRepository $adRepository) {
$this->adRepository = $adRepository;
}
/**
* @return \Nette\Application\IRouter
*/
public function createRouter()
{
$router = new RouteList();
$router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default');
$router[] = new Route('<presenter>/<action>/<id>', array(
'presenter' => 'Homepage',
'action' => 'detail',
'id' => array(
'Route::VALUE' => 0, // default value
'Route::FILTER_OUT' => callback('adRepository::getTitleById'),
),
));
return $router;
}
}
?>
Nic se neděje. URL pořád ve tvaru localhost/homepage/detail/0.
- jiri.pudil
- Nette Blogger | 1029
- Ten callback stejně vytváříš statický. Musíš ho navázat na tu instanci, kterou si injektuješ.
- Route::VALUE a Route::FILTER_OUT jsou konstanty třídy Route. Apostrofy okolo nich nemají co dělat.
Ergo nějak takhle by to mělo fungovat:
'id' => array(
Route::VALUE => 0,
Route::FILTER_OUT => array($this->adRepository, 'getTitleById'),
),
A jedno doporučení na závěr: v čemkoliv, co není presenter, je
nejvhodnější použít injektování přes konstruktor, tzn. metodu
inject
přejmenuj na __construct
(a proměnná
$adRepository
by měla být private).
- Checkpoint
- Člen | 34
jiri.pudil napsal(a):
- Ten callback stejně vytváříš statický. Musíš ho navázat na tu instanci, kterou si injektuješ.
- Route::VALUE a Route::FILTER_OUT jsou konstanty třídy Route. Apostrofy okolo nich nemají co dělat.
Ergo nějak takhle by to mělo fungovat:
'id' => array( Route::VALUE => 0, Route::FILTER_OUT => array($this->adRepository, 'getTitleById'), ),
A jedno doporučení na závěr: v čemkoliv, co není presenter, je nejvhodnější použít injektování přes konstruktor, tzn. metodu
inject
přejmenuj na__construct
(a proměnná$adRepository
by měla být private).
Děkuji za rychlý feedback a tipy, ale teď mi zase vyhazuje laděnka chybu.
Pokud nechám metodu inject(), tak laděnka vyhodí error.
call_user_func() expects parameter 1 to be a valid callback, first array member is not a valid class name or object
Pokud použiji metodu __construct, tak vyhodí zase jiný error.
Nette\DI\ServiceCreationException
Service ‚22_App_RouterFactory‘: No service of type App\Model\AdRepository found. Make sure the type hint in App\RouterFactory::__construct() is written correctly and service of this type is registered.
Přitom službu Model\AdRepository mám zaregistrovanou v config.neon.
- Pavel Macháň
- Člen | 282
Checkpoint napsal(a):
Děkuji za rychlý feedback a tipy, ale teď mi zase vyhazuje laděnka chybu.
Pokud nechám metodu inject(), tak laděnka vyhodí error.
call_user_func() expects parameter 1 to be a valid callback, first array member is not a valid class name or object
Pokud použiji metodu __construct, tak vyhodí zase jiný error.
Nette\DI\ServiceCreationException
Service ‚22_App_RouterFactory‘: No service of type App\Model\AdRepository found. Make sure the type hint in App\RouterFactory::__construct() is written correctly and service of this type is registered.
Přitom službu Model\AdRepository mám zaregistrovanou v config.neon.
Dej si pozor na Namespace… v erroru to máš i napsaný
Router má namespace App; takže pokud napíšeš
namespace App;
__construct(Model\AdRepository $repository) // = App\Model\AdRepository
// musí to být
__construct(\Model\AdRepository $repository) // = Model\AdRepository
ale ty to máš mít jen Model\AdRepository takže musíš uvést \Model\AdRepository … to \ je tam důležité
Pokud nemáš na začátku lomítko tak se to automaticky bere dle namespace které je uvedeno. Pokud uvedeš lomítko, řekneš tomu, že se jede od „začátku“. Pokud bys chtěl uvést bez lomítka aby to šlo musel bys použít use Model;
namespace App;
use Model;
__construct(Model\AdRepository $repository) // = Model\AdRepository
Editoval EIFEL (9. 2. 2014 14:15)
- Checkpoint
- Člen | 34
EIFEL napsal(a):
Checkpoint napsal(a):
jiri.pudil napsal(a):
- Ten callback stejně vytváříš statický. Musíš ho navázat na tu instanci, kterou si injektuješ.
- Route::VALUE a Route::FILTER_OUT jsou konstanty třídy Route. Apostrofy okolo nich nemají co dělat.
Ergo nějak takhle by to mělo fungovat:
'id' => array( Route::VALUE => 0, Route::FILTER_OUT => array($this->adRepository, 'getTitleById'), ),
A jedno doporučení na závěr: v čemkoliv, co není presenter, je nejvhodnější použít injektování přes konstruktor, tzn. metodu
inject
přejmenuj na__construct
(a proměnná$adRepository
by měla být private).Děkuji za rychlý feedback a tipy, ale teď mi zase vyhazuje laděnka chybu.
Pokud nechám metodu inject(), tak laděnka vyhodí error.
call_user_func() expects parameter 1 to be a valid callback, first array member is not a valid class name or object
Pokud použiji metodu __construct, tak vyhodí zase jiný error.
Nette\DI\ServiceCreationException
Service ‚22_App_RouterFactory‘: No service of type App\Model\AdRepository found. Make sure the type hint in App\RouterFactory::__construct() is written correctly and service of this type is registered.
Přitom službu Model\AdRepository mám zaregistrovanou v config.neon.
Dej si pozor na Namespace… v erroru to máš i napsaný
Router má namespace App; takže pokud napíšešnamespace App; __construct(Model\AdRepository $repository) // = App\Model\AdRepository
ale ty to máš mít jen Model\AdRepository takže musíš uvést \Model\AdRepository … to \ je tam důležité
Už jsem na to došel, akorát jsem chtěl upravit předchozí příspěvěk.
:)
Každopádně neustále přetrvává první chybka. :X
- jiri.pudil
- Nette Blogger | 1029
Co je první chybka? Neplatný callback? V tom případě musíš injektovat přes konstruktor (resp. jsou i jiné způsoby, ale tenhle je nejjednodušší a nejčistší).
- Checkpoint
- Člen | 34
Tak jsem teda dal na Vaši radu a injektoval jsem přes konstruktor.
Všechno funguje, ale pořád je URL ve stejném tvaru. Po názvu postu z DB
ani stopy.
- jiri.pudil
- Nette Blogger | 1029
Všechno funguje, ale pořád je URL ve stejném tvaru. Po názvu postu z DB ani stopy.
Máš tam dvě de facto stejné routy, takže se ti matchne hned ta první a ke druhé, kde máš filter funkci, už to vůbec nedojde.
- Pavel Macháň
- Člen | 282
Checkpoint napsal(a):
Tak jsem teda dal na Vaši radu a injektoval jsem přes konstruktor.
Všechno funguje, ale pořád je URL ve stejném tvaru. Po názvu postu z DB ani stopy.
Routy se čtou v pořadí jak je tam vkládáš. To znamená, že se veme první routa která vyhovuje a další se už ignorují (v debug panelu jsou označeny: may).
Nejdřív si dej na první místo tvoje specifické routy a všeobecné dej až na konec jako poslední možnost.
- Checkpoint
- Člen | 34
Za lomítkem se nezobrazí nic. Tedy adresa je ve tvaru localhost/inzerat/detail-inzeratu/
<?php
public function createRouter()
{
$router = new RouteList();
$router[] = new Route('inzerat/detail-inzeratu/<id>', array(
'presenter' => 'Homepage',
'action' => 'detail',
'id' => array(
Route::VALUE => 0,
Route::FILTER_OUT => array($this->adRepository, 'getTitleById'),
),
));
$router[] = new Route('[<presenter>/]<action>[/<id>]', 'Homepage:default');
return $router;
}
?>
- Checkpoint
- Člen | 34
Tak jsem se s tím chvíli hrál, až jsem se dostal do fáze, kdy mi laděnka vypisuje
rawurldecode() expects parameter 1 to be string, object given
Funkce rawurldecode() očekává parametr 1 jako string, ale já dodávám objekt? wtf
- David Matějka
- Moderator | 6445
nevim, s cim vsim sis hral a jak je tedy kod, ktery si posilal aktualni, ale
public function getTitleById($id) {
return $this->database->query('SELECT title FROM ad WHERE idAd = ?', $id); //získání title
}
nevraci title, ale ResultSet, zkus za to pridat
->fetch()->title
- Checkpoint
- Člen | 34
Tak jsem tedy zkusil přidat ->fetch()->title.
Skončí to u erroru Trying to get property of non-object.
- Checkpoint
- Člen | 34
Laděnka mi ale vyhazuje error, když chci přistupovat ke sloupci v jiné
tabulce.
Tohle je červeně označeno.
<a>Lokalita – <?php echo Nette\Templating\Helpers::escapeHtml($posledni->locality->title, ENT_NOQUOTES) ?></a>
Pokud metodu getTitleByUrl() nezavolám, tak všechno funguje v pořádku.
- MartinitCZ
- Člen | 580
@**Checkpoint**: Po query() nemůžeš mít ->fetch().
Pořádně si přečti https://doc.nette.org/cs/database
- David Matějka
- Moderator | 6445
@martinit: query vraci ResultSet, nad kterym fetch volat muzes
@Checkpoint: hod sem ladenku
- Checkpoint
- Člen | 34
Zde je screen laděnky.
https://www.dropbox.com/…r_nette_.jpg
Přikládám ještě zdrojové kódy
AdRepository.php
<?php
namespace Model;
class AdRepository extends Repository {
public function getAll() {
return $this->database->table('ad');
}
public function getDetail($id) {
return $this->database->table('ad')->where('idAd = ?', $id);
}
public function getIdByUrl() {
}
public function getTitleById($id) {
return $this->database->query('SELECT slug FROM ad WHERE idAd = ?', $id)->fetch()->slug;
}
}
?>
RouterFactory.php
<?php
namespace App;
use Model;
use Nette,
Nette\Application\Routers\RouteList,
Nette\Application\Routers\Route,
Nette\Application\Routers\SimpleRouter;
/**
* Router factory.
*/
class RouterFactory
{
private $adRepository;
public function __construct(Model\AdRepository $adRepository) {
$this->adRepository = $adRepository;
}
/**
* @return \Nette\Application\IRouter
*/
public function createRouter()
{
$router = new RouteList();
$router[] = new Route('vsechny-obory/', array(
'presenter' => 'Obor',
'action' => 'default',
'id' => NULL,
));
$router[] = new Route('vsechny-obory/obor/<id>', array(
'presenter' => 'Obor',
'action' => 'detail',
'id' => NULL,
));
$router[] = new Route('inzerat/detail-inzeratu/<id>', array(
'presenter' => 'Homepage',
'action' => 'detail',
'id' => array(
Route::VALUE => 0,
Route::FILTER_OUT => array($this->adRepository, 'getTitleById'),
),
));
$router[] = new Route('[<presenter>/]<action>[/<id>]', 'Homepage:default');
return $router;
}
}
?>
Editoval Checkpoint (11. 2. 2014 11:04)
- David Matějka
- Moderator | 6445
a existuje zaznam s tim id?
dej si tam kontrolu, jak jsem ti rikal.
public function getTitleById($id) {
$query = $this->database->query('SELECT slug FROM ad WHERE idAd = ?', $id);
if($row = $query->fetch()) {
return $row->slug;
}
return NULL;
}
- Checkpoint
- Člen | 34
V odkazu už se ukazuje titulek, super.
Ale snaží se přejít na DetailPresenter, ne na akci ‚detail‘.
- David Matějka
- Moderator | 6445
- pravdepodobne budes chtit do presenteru prijmout ID jako cislo, ne? dej si tam tedy i FILTER_IN, ktery prelozi slug na id
- na jakou url se to dotazovalo?
- Checkpoint
- Člen | 34
Už to frčí, super.
Pro zajímavost, kdyby někdo hledal, tak jsem vytvořil metodu
getIdByURL().
A pomocí FILTER_IN přeložil Slug na ID.
Zde je metoda v AdRepository.php
<?php
public function getIdByUrl($slug) {
$query = $this->database->query('SELECT idAd FROM ad WHERE slug = ?', $slug);
if($row = $query->fetch()) {
return $row->idAd;
}
return NULL;
}
?>
Zde je routa v RouterFactory.php
<?php
...
$router[] = new Route('inzerat/detail-inzeratu/<id>', array(
'presenter' => 'Homepage',
'action' => 'detail',
'id' => array(
Route::FILTER_IN => array($this->adRepository, 'getIdByUrl'),
Route::FILTER_OUT => array($this->adRepository, 'getTitleById'),
),
));
...
?>
Díky všem zůčastněným za pomoc. :)