Název článku v URL podle ID
- umrlec
- Člen | 56
Zdravím, začínám s Nette a chci si vytvořit jednoduchý blog. No a samozřejmě chci, aby místo například example.com/clanky/5 byla URL example.com/clanky/nazev-clanku-s-id-5 . Takže…
mám model ArticlesModel a v něm metodu getTitleById
<?php
final class ArticlesModel extends BaseModel
{
public function getTitleById($id)
{
// Tady z databáze tahám název článku, nyní pro jednoduchost např. takhle:
return $id * 2;
}
}
?>
a v bootstrap.php mám tuto routu (podle Davida (hier http://jdem.cz/hf3g7) by to mělo jít psát takhle)
<?php
$router[] = new Route('clanky/<id>', array(
'presenter' => 'Frontend:Articles',
'action' => 'detail',
'id' => array(
'Route::VALUE' => 0, // default value
'Route::FILTER_OUT' => callback('ArticlesModel::getTitleById'),
),
));
?>
A stále mi to vytváří routy ve tvaru example.com/clanky/5 (podle ‚algoritmu‘ zde ukázkové metody getTitleById by měl vracet 10). Prosím tedy o radu, popřípadě jestli na to jdu špatnou cestou, tak o navedení na správnou. Díky
Nette používám ve verzi 1.0-alpha ze dne 17. 9. 2010 pro PHP 5.3; app/temp/ promazávám
Editoval umrlec (23. 9. 2010 11:20)
- uestla
- Backer | 799
Ahoj, podle Davida to máš až na ty uvozovky kolem klíčů pole – tedy
$router[] = new Route('clanky/<id>', array(
'presenter' => 'Frontend:Articles',
'action' => 'detail',
'id' => array(
'Route::VALUE' => 0, // default value
'Route::FILTER_OUT' => callback('ArticlesModel::getTitleById'),
),
));
zkus nahradit za
$router[] = new Route('clanky/<id>', array(
'presenter' => 'Frontend:Articles',
'action' => 'detail',
'id' => array(
Route::VALUE => 0, // default value
Route::FILTER_OUT => callback('ArticlesModel::getTitleById'),
),
));
- umrlec
- Člen | 56
@uestla: Díky, dám si facku.
Teď mám zase ale problém s funkcí getTitleById…
<?php
final class ArticleModel extends BaseModel
{
public function getTitleById($id)
{
return $this->connection->fetchSingle('SELECT [article_title] FROM [articles] WHERE [article_id]=%i', $id);
}
}
?>
ta proměnná connection je z BaseModelu, který vypadá takto:
<?php
abstract class BaseModel extends Object
{
protected static $connection;
public static function initializeConnection()
{
self::$connection = dibi::connect(Environment::getConfig('database'));
}
public function getConnection()
{
return self::$connection;
}
}
?>
a v bootstrap.php mám tedy
<?php
$router[] = new Route('clanky/<id>', array(
'presenter' => 'Frontend:Articles',
'action' => 'detail',
'id' => array(
Route::VALUE => 0, // default value
Route::FILTER_OUT => callback('ArticleModel::getTitleById'),
),
));
?>
Tohle mi ale hází fatal error „Using $this when not in object context“ v té metodě getTitleById. Dovolím si poprosit o ještě jednu radu.
- uestla
- Backer | 799
Chyba vše vysvětluje… Jako callback voláš statickou metodu (která ke
všemu není uvozená slovíčkem static
), která se tváří, že
je volána na objektu – ten ovšem není vytvořen.
Čili bych v modelu vytvořil statickou metodu a místo
$this->connection->...
použil
klasické dibi::query(...)
- westrem
- Člen | 398
To je predsa ale problem, ktory vyplyva z neznalosti PHP a nie nette.
Nastavujes predsa staticky callback
ArticleModel::getTitleById
a to je potom jasne, ze v tej funkcii
nemozes volat $this
.
Celkovo ako sa pozeram na ten kod tak pleties staticke veci z nestatickymi.
Prepis ten riadok na toto:
return self::getConnection()->fetchSingle(' ... ');
- umrlec
- Člen | 56
Tak takhle to funguje, avšak nesedí mi vytváření nového připojení, když už ho jednou vytvářím v BaseModelu, když ale řádek dibi::connect(Nette\Envi… vynechám, tak systém háže DibiException „Dibi is not connected to database.“
<?php
public static function getTitleById($id)
{
dibi::connect(Nette\Environment::getConfig('database'));
return String::webalize(dibi::query('SELECT [article_title] FROM [articles] WHERE [article_id]=%i', $id)->fetchSingle());
}
?>
- umrlec
- Člen | 56
westrem napsal(a):
To je predsa ale problem, ktory vyplyva z neznalosti PHP a nie nette.
Nastavujes predsa staticky callback
ArticleModel::getTitleById
a to je potom jasne, ze v tej funkcii nemozes volat$this
.Celkovo ako sa pozeram na ten kod tak pleties staticke veci z nestatickymi.
Prepis ten riadok na toto:
return self::getConnection()->fetchSingle(' ... ');
<?php
public static function getTitleById($id)
{
return String::webalize(self::getConnection()->fetchSingle('SELECT [article_title] FROM [articles] WHERE [article_id]=%i', $id));
}
?>
Fatal Error: Call to a member function fetchSingle() on a non-object …
Editoval umrlec (23. 9. 2010 14:21)
- umrlec
- Člen | 56
Už to jde, nakonec mi poradili jinde, ale řešení vychází z toho Tvého
(@westrem). V getConnection jsem ještě musel přidat podmínku,
ověřující, zda je proměnná $connection
nastavená a pokud ne,
vytvoří se spojení a přiřadí se jí. Nevím no proč se mi to spojení
neotevře z té metody initializeConnection z $application->onStartup[].
Pak musím také nějak získat zpátky to id článku, takže si nakonec ukládám webalized title do databáze a v routě pak mám ještě filtr FILTER_IN volající metodu getIdByTitle(). Bez toho mi to házelo nějaké errory v detailu článku. Takže dohromady to vypadá takhle:
BaseModel.php
<?php
abstract class BaseModel extends Object
{
public static $connection = NULL;
public static function initializeConnection()
{
dibi::connect(Environment::getConfig('database'));
}
public static function getConnection()
{
if(!isset(self::$connection)) {
self::$connection = dibi::connect(Environment::getConfig('database'));
}
return self::$connection;
}
}
?>
ArticleModel.php
<?php
final class ArticleModel extends BaseModel
{
public static function getTitleById($id)
{
return self::getConnection()->fetchSingle('SELECT [article_title_webalized] FROM [articles] WHERE [article_id]=%i', $id);
}
public static function getIdByTitle($title)
{
return self::getConnection()->fetchSingle('SELECT [article_id] FROM [articles] WHERE [article_title_webalized]=%s', $title);
}
}
?>
Routa v bootstrap.php
<?php
$router[] = new Route('clanky/<id>', array(
'presenter' => 'Frontend:Articles',
'action' => 'detail',
'id' => array(
Route::VALUE => 0, // default value
Route::FILTER_OUT => callback('ArticleModel::getTitleById'),
Route::FILTER_IN => callback('ArticleModel::getIdByTitle'),
),
));
?>
- v kroku 3
<?php
$application = Environment::getApplication()->onStartup[] = 'BaseModel::initializeConnection';
?>
Editoval umrlec (23. 9. 2010 18:26)
- arron
- Člen | 464
umrlec napsal(a):
Nevím no proč se mi to spojení neotevře z té metody initializeConnection z $application->onStartup[].
Ono se to spojeni velmi pravdepodobne otevre, ale zhledem k tomu, ze ho
nikam nepriradis (treba do te promene self::$connection
), tak ho
tam pak nenajdes ;-)
- redhead
- Člen | 1313
umrlec napsal(a):
Nevím no proč se mi to spojení neotevře z té metody initializeConnection z $application->onStartup[].
Ono se otevře, ale už ji nesetuješ do té proměnné $connection, takže se v getConnection vytvoří znova (podmínka není splněna)
Edit: pozdě..
Editoval redhead (23. 9. 2010 18:34)
- umrlec
- Člen | 56
Tak dobře tedy:
<?php
use Nette\Debug;
use Nette\Environment;
use Nette\Application\Route;
use Nette\Application\SimpleRouter;
// Step 1: Load Nette Framework
// this allows load Nette Framework classes automatically so that
// you don't have to litter your code with 'require' statements
require LIBS_DIR . '/Nette/loader.php';
// Step 2: Configure environment
// 2a) enable Nette\Debug for better exception and error visualisation
Debug::enable();
// 2b) load configuration from config.ini file
Environment::loadConfig();
// Step 3: Configure application
// Step 3a: get and setup a front controller
$application = Environment::getApplication();
$application->errorPresenter = 'Error';
// Step 3b: initialize database connection
$application->onStartup[] = 'BaseModel::initializeConnection';
// Step 4: Setup application router
$router = $application->getRouter();
$router[] = new Route('index.php', array(
'presenter' => 'Frontend:Homepage',
'action' => 'default',
), Route::ONE_WAY);
$router[] = new Route('clanky/<id>', array(
'presenter' => 'Frontend:Articles',
'action' => 'detail',
'id' => array(
Route::VALUE => 0, // default value
Route::FILTER_OUT => callback('ArticleModel::getTitleById'),
Route::FILTER_IN => callback('ArticleModel::getIdByTitle'),
),
));
$router[] = new Route('<presenter>/<action>/<id>', array(
'presenter' => 'Frontend:Homepage',
'action' => 'default',
'id' => NULL,
));
// Step 5: Run the application!
$application->run();
?>
Editoval umrlec (23. 9. 2010 19:48)
- arron
- Člen | 464
Tak ted uz budu varit z vody:
zkus jenom tak cvicne odstranit u BaseModelu to abstract
…
jestli to bude mit nejaky vliv. Ale kdyby se to takhle nedalo volat, tak by
nejspis Nette zarvalo. Jinak me nic moc nenapada.
Jo a jeste si zmen tu podminku v getConnection
, at misto
dibi::connect
zavola to initializeConnection
. Zmizi Ti
tam pak ta oskliva duplicita volani dibi::connect
:-)
- westrem
- Člen | 398
Okej skus to takto:
abstract class BaseModel extends Object
{
public static $connection = NULL;
public static function initializeConnection()
{
self::$connection = dibi::connect(Environment::getConfig('database'));
}
public static function getConnection()
{
if (self::$connection === NULL) {
self::initializeConnection();
}
return self::$connection;
}
}
Zrus toto:
$application->onStartup[] = 'BaseModel::initializeConnection';
A napis original tohto:
Fu*king Exception #1 : Něco se ti posr***
- umrlec
- Člen | 56
Vlastně ano, takhle když nevolám initializeConnection() v bootstrapu (což by mi připadalo lepší, ale tak když to nejde, tak to nejde), tak mi příjde v podstatě zbytečná. Takže teď mám BaseModel takhle a šlape to.
<?php
use Nette\Object;
use Nette\Environment;
abstract class BaseModel extends Object
{
public static $connection = NULL;
public static function getConnection()
{
if (self::$connection === NULL) {
self::$connection = dibi::connect(Environment::getConfig('database'));
}
return self::$connection;
}
}
?>
- westrem
- Člen | 398
což by mi připadalo lepší, ale tak když to nejde, tak to nejde
Priznam sa, ze vynimocne sa mi nechcelo patrat po tom, preco ti to v boot-e neslo, istotne by to bola podla mna ale nejaka trivialita.
To, ze to bolo v boot-e neznamena nutne, ze to bolo lepsie. Takto to mas pekne lazy-load a ak by v buducnu prisiel nejaky poziadavok, ktory vobec nevyuziva BaseModel a DB tak sa k nej ani nepripajas a nenavazujes zbytocny connection – rychlejsie spracovanie poziadavku ;)