Jak na Router aby místo id vrace titulek

jAkErCZ
Člen | 321
+
0
-

Zdravím,
Mám routu

$router[] = new Route('[<locale=cs cs|en>/]<action>', [
            'presenter' => 'Front:Works',
            'action' => [
                Route::FILTER_STRICT => true,
                Route::FILTER_TABLE => [
                    // řetězec v URL => akce presenteru
                    'works' => 'default',
                    'detail' => 'detail'
                ]
            ]
        ]);

který vrací url ve tvaru: portfolio/web/detail?work_id=1

Ale rád bych aby místo toho ?work_id=1 to vracelo něco jako název-id aby ty url vypadaly prostě lépe.

Díky všem za rady :)

Kamil Valenta
Člen | 758
+
0
-
'[<locale=cs cs|en>/]<action>/<slug>'

A dopíšeš si Route::FILTER_IN, která ověří, zda ten slug odpovídá nějakému ID.

jAkErCZ
Člen | 321
+
0
-

kamil_v napsal(a):

'[<locale=cs cs|en>/]<action>/<slug>'

A dopíšeš si Route::FILTER_IN, která ověří, zda ten slug odpovídá nějakému ID.

Mohl by si mi ukázat nějaký případ? :)

MajklNajt
Člen | 471
+
0
-

@jAkErCZ skús trošku potrápiť aj svoju hlavu: https://doc.nette.org/…tion/routing#…

jAkErCZ
Člen | 321
+
0
-

MajklNajt napsal(a):

@jAkErCZ skús trošku potrápiť aj svoju hlavu: https://doc.nette.org/…tion/routing#…

No díval sem se na hromadu návodů ale nějak to prostě nechápu.

$router[] = new Route('[<locale=cs cs|en>/]<action>/<slug>', [
            'presenter' => 'Front:Works',
            'action' => [
                Route::FILTER_STRICT => true,
                Route::FILTER_TABLE => [
                    // řetězec v URL => akce presenteru
                    'works' => 'default',
                    'detail' => 'detail'
                ]
            ],
            'slug' => [
                Route::FILTER_IN => function ($id){
                    if(is_numeric($id)){
                        return $id;
                    }else{
                        bdump($id);
                        $page = $this->worksRepository->getWorkBySlug($id);
                        return $page->id;
                    }
                },
                Route::FILTER_OUT => function ($id){
                    if(!is_numeric($id)){
                        return $id;
                    }else{
                        $page = $this->productFrontRepository->getWorkBySlug($id);
                        bdump($page);
                        exit;
                        return $page->slug;
                    }
                }
            ]
        ]);

A model

public function getWorkBySlug (int $work_id)
{
    $work = $this->database->table(self::TABLE_NAME)->where(self::COLUMN_ID, $work_id)->fetchPairs($work_id,'title');
    return $work;
    // Id článku je 2 => url = lesy
    // Vrácení předem vytvořené url | titulek článku upravený přez Nette\Utils\Strings::webalize
}

Co teda dělám špatně vycházel jsem z tohoto a toho co mi tu psal kolega. URL

A po pužití tohoto tak url vypadá takto: web/front.works/detail?work_id=1

A nebo ještě ZDE COOL URL ale také to nefunguje.

Editoval jAkErCZ (4. 4. 2020 15:52)

David Matějka
Moderator | 6445
+
0
-

v route pracujes s parametrem slug a dle vseho generujes URL s parametrem work_id

jAkErCZ
Člen | 321
+
0
-

David Matějka napsal(a):

v route pracujes s parametrem slug a dle vseho generujes URL s parametrem work_id

No tady je presenter

public function actionDetail($work_id){
    $work = $this->worksRepository->getWork($work_id);
    if ($work_id){
        $this->template->work = $work;
    }else{
        $this->redirect(':Front:Homepage:');
    }
}

Prostě ať vyzkouším vše na co jsem narazil tak to nefunguje prostě chci aby místo toho /detail?work_id=1 vracel /detail/1-Název ale prostě nenapadá mi normální řešení.

Mysteria
Člen | 797
+
+1
-

Třeba takhle:

URL budeš mít třeba http://127.0.0.1:8080/homepage/default/slug-two a v presenteru budeš mít místo slug-two normálně 2. Jenom si tam teda musíš místo toho pole dát databázi, ale to už by snad neměl být problém.

final class HomepagePresenter extends Nette\Application\UI\Presenter
{

    public function actionDefault(string $slug): void
    {
        bdump('ID: ' . $slug);
    }

}
final class RouterFactory
{

    use Nette\StaticClass;

    private const TABLE = [
        1 => 'slug-one',
        2 => 'slug-two',
    ];

    public static function createRouter(): RouteList
    {
        return (new RouteList())->addRoute(
            '<presenter>/<action>[/<slug>]',
            [
                'presenter' => 'Homepage',
                'action'    => 'default',
                NULL        => [
                    Route::FILTER_IN  => static function (array $parameters): array {
                        $parameters['slug'] = array_search($parameters['slug'], self::TABLE);

                        return $parameters;
                    },
                    Route::FILTER_OUT => static function (array $parameters): array {
                        $parameters['slug'] = self::TABLE[$parameters['slug']];

                        return $parameters;
                    },
                ],
            ]
        );
    }

}
jAkErCZ
Člen | 321
+
0
-

Mysteria napsal(a):

Třeba takhle:

URL budeš mít třeba http://127.0.0.1:8080/homepage/default/slug-two a v presenteru budeš mít místo slug-two normálně 2. Jenom si tam teda musíš místo toho pole dát databázi, ale to už by snad neměl být problém.

final class HomepagePresenter extends Nette\Application\UI\Presenter
{

    public function actionDefault(string $slug): void
    {
        bdump('ID: ' . $slug);
    }

}
final class RouterFactory
{

    use Nette\StaticClass;

    private const TABLE = [
        1 => 'slug-one',
        2 => 'slug-two',
    ];

    public static function createRouter(): RouteList
    {
        return (new RouteList())->addRoute(
            '<presenter>/<action>[/<slug>]',
            [
                'presenter' => 'Homepage',
                'action'    => 'default',
                NULL        => [
                    Route::FILTER_IN  => static function (array $parameters): array {
                        $parameters['slug'] = array_search($parameters['slug'], self::TABLE);

                        return $parameters;
                    },
                    Route::FILTER_OUT => static function (array $parameters): array {
                        $parameters['slug'] = self::TABLE[$parameters['slug']];

                        return $parameters;
                    },
                ],
            ]
        );
    }

}

Tak tvá rada funguje dobře ale mám problém s tím že mi to háže Undefined index: slug ale když dám ignorovat chybu jede to kde to mám tedy definovat?

A ještě upravil jsem si to tedy na DB a našel jsem další problém

NULL => [
               Route::FILTER_IN  => static function (array $parameters): array {
                   $parameters['slug'] = array_search($parameters['slug'], $this->worksRepository->getWorkBySlug());
                   return $parameters;
               },
               Route::FILTER_OUT => static function (array $parameters): array {
                   $parameters['slug'] = $this->worksRepository->getWorkBySlug()[$parameters['slug']];
                   return $parameters;
               },
           ]

Chyba:

Using $this when not in object context

A přitom jsem zkusil i trik co psaly u jiných příspěvků a to upravit config

# Nastavení služeb v rámci celé aplikace.
services:
	- App\RouterFactory
	router: @App\RouterFactory::createRouter()

a nic

Editoval jAkErCZ (4. 4. 2020 20:25)

Kamil Valenta
Člen | 758
+
0
-

Nestačilo by dát ze „static function“ pryč „static“?

Mysteria
Člen | 797
+
0
-

Přesně tak, dej pryč to static, pak ti to bude fungovat. A to slug záleží jak to máš pojmenovaný v předpisu routy. Tzn pokud tam máš třeba '<presenter>/<action>[/<mujSuperParameter>]', tak tam budeš používat $parameters['mujSuperParameter'] a v presenteru taky pak public function actionDefault(string $mujSuperParameter).

Plus bys neměl zapomenout na ošetření toho, když ti někdo ručně upraví adresu na nějakou blbost, aby se to chovalo korektně, když ti to nic nenajde v databázi (nějakej default třeba).

jAkErCZ
Člen | 321
+
0
-

Mysteria napsal(a):

Přesně tak, dej pryč to static, pak ti to bude fungovat. A to slug záleží jak to máš pojmenovaný v předpisu routy. Tzn pokud tam máš třeba '<presenter>/<action>[/<mujSuperParameter>]', tak tam budeš používat $parameters['mujSuperParameter'] a v presenteru taky pak public function actionDefault(string $mujSuperParameter).

Plus bys neměl zapomenout na ošetření toho, když ti někdo ručně upraví adresu na nějakou blbost, aby se to chovalo korektně, když ti to nic nenajde v databázi (nějakej default třeba).

Ať dám static pryč nebo ne tak stále to hází Using $this when not in object context

řádek

$parameters['slug'] = $this->worksRepository->getWorkBySlug()[$parameters['slug']];

Fakt už nevím co dělám špatně :(

Kamil Valenta
Člen | 758
+
0
-

Mysteria napsal(a):

a v presenteru taky pak public function actionDefault(string $mujSuperParameter).

V presenteru už bych používal ID, na které se mi slug v routeru přeloží. „mujSuperParameter“ coby slug může být závislý třeba na jazykové mutaci, ID už bude nezávislé.

Plus bys neměl zapomenout na ošetření toho, když ti někdo ručně upraví adresu na nějakou blbost, aby se to chovalo korektně, když ti to nic nenajde v databázi (nějakej default třeba).

O to se může postarat už ta FILTR IN, když nenajde odpovídající záznam a vrátí null, routa to ani nematchne a pokud to nematchne ani žádná jiná, padne to do 404.

Editoval kamil_v (4. 4. 2020 23:45)

Kamil Valenta
Člen | 758
+
0
-

jAkErCZ napsal(a):

Ať dám static pryč nebo ne tak stále to hází Using $this when not in object context
Fakt už nevím co dělám špatně :(

Ukaž zase asi celou routu.

Šaman
Člen | 2634
+
0
-

Zkus si před tou anonymní funkcí definovat

$presenter = $this;

a pak v definici funkce použít use

Route::FILTER_OUT => function (array $parameters) use($presenter): array {

Anebo ještě lépe předávat přímo $worksRepository.

Marek Bartoš
Nette Blogger | 1165
+
0
-

Ať dám static pryč nebo ne tak stále to hází Using $this when not in object context

Ve statických funkcích nemůžeš volat $this. Jsi v kontextu třídy, ne objektu. I když smažeš static z anonymní funkce, tak jsi pořád ve statické metodě createRouter(). Tzn. ani jedno nesmí být static, pak ti $this bude fungovat.

jAkErCZ
Člen | 321
+
0
-

kamil_v napsal(a):

jAkErCZ napsal(a):

Ať dám static pryč nebo ne tak stále to hází Using $this when not in object context
Fakt už nevím co dělám špatně :(

Ukaž zase asi celou routu.

Tady je celý router

class RouterFactory
{
	use StaticClass;

    /** @var  WorksRepository */
    public $worksRepository;

    public function __construct(WorksRepository $worksRepository)
    {
        $this->worksRepository = $worksRepository;
    }

	/**
	 * Vytváří a vrací seznam routovacích pravidel pro aplikaci.
	 * @return IRouter výsledný router pro aplikaci
	 */
	public static function createRouter()
	{
		$router = new RouteList;
		$router[] = new Route('[<locale=cs cs|en>/]kontakt', 'Front:Contact:default');
        /* Administrace */
        $router[] = new Route('admin/[<locale=cs cs|en>/]<presenter>/<action>[/<id [0-9]+>]', array(
            'module' => 'Admin',
            'presenter' => 'Home',
            'action' => 'default',
            'id' => NULL,
        ));
		$router[] = new Route('[<locale=cs cs|en>/]<action>', [
			'presenter' => 'Front:User',
			'action' => [
				Route::FILTER_STRICT => true,
				Route::FILTER_TABLE => [
					// řetězec v URL => akce presenteru
					'profil' => 'default',
					'prihlaseni' => 'login',
					'odhlasit' => 'logout',
					'registrace' => 'register'
				]
			]
		]);
        $router[] = new Route('[<locale=cs cs|en>/]<action>[/<slug>]', [
            'presenter' => 'Front:Works',
            'action' => [
                Route::FILTER_STRICT => true,
                Route::FILTER_TABLE => [
                    // řetězec v URL => akce presenteru
                    'works' => 'default',
                    'detail' => 'detail'
                ]
            ],
            NULL => [
                Route::FILTER_IN  => static function (array $parameters): array {
                    $parameters['slug'] = array_search($parameters['slug'], $this->worksRepository->getWorkBySlug());
                    return $parameters;
                },
                Route::FILTER_OUT => static function (array $parameters): array {
                    $parameters['slug'] = $this->worksRepository->getWorkBySlug()[$parameters['slug']];
                    return $parameters;
                },
            ]
        ]);

        $router[] = new Route("[<locale=cs cs|en>/]<presenter>/<action>[/<id>]", "Front:Homepage:default");
		return $router;
	}
}

a presenter

public function actionDetail(string $slug){
        $work = $this->worksRepository->getWork($slug);
        if ($slug){
            $this->template->work = $work;
        }else{
            $this->redirect(':Front:Homepage:');
        }
    }
MajklNajt
Člen | 471
+
0
-

nechcem byť zlý, ale

  1. robíš špatne to, že začínaš od konca, treba doštudovať základy OOP – v statickej triede nemá čo hľadať konštruktor
  2. treba poriadne čítať, napr. dokumentáciu https://doc.nette.org/…tion/routing#… – Jakékoliv závislosti, třeba na databázi, nebo příznak, lze předat do továrny routeru jako parametry
jAkErCZ
Člen | 321
+
0
-

MajklNajt napsal(a):

nechcem byť zlý, ale

  1. robíš špatne to, že začínaš od konca, treba doštudovať základy OOP – v statickej triede nemá čo hľadať konštruktor
  2. treba poriadne čítať, napr. dokumentáciu https://doc.nette.org/…tion/routing#… – Jakékoliv závislosti, třeba na databázi, nebo příznak, lze předat do továrny routeru jako parametry

Chápu :)

NULL => [
               Route::FILTER_IN  => static function (array $parameters) use ($worksRepository): array {
                   $parameters['id'] = array_search($parameters['id'], $worksRepository->getWorkBySlug());
                   return $parameters;
               },
               Route::FILTER_OUT => static function (array $parameters) use ($worksRepository): array {
                   bdump($worksRepository->getWorkBySlug());
                   $parameters['id'] = $worksRepository->getWorkBySlug()[$parameters['id']];
                   return $parameters;
               },
           ]

Do modelu už se to normálně napojí a vytáhne ale háže mi to stále

Undefined index: id

$parameters['id'] = $worksRepository->getWorkBySlug()[$parameters['id']];

A ještě mě napadlo jak využiju webalize když Model mám takto

public function getWorkBySlug ()
{
    return $this->database->table(self::TABLE_NAME)->fetchPairs('work_id','title');
}

Díky za radu

David Matějka
Moderator | 6445
+
0
-

a proc zas pracujes s parametrem id, kdyz pred tim bylo videt, ze mas v definici routy slug? pak se nediv, ze index neexistuje. prijde mi, ze vubec nevis, co se deje, a snazis se to vyresit nahodnyma upravama kodu. projdi si v klidu dokumentaci, pohledej na foru, kde se to hodnekrat resilo, koukni na nejaky videa z poslednich sobot, kde se routování probíralo…

a vytahnout si z databaze vsechny prispevky, abys zjistil, kde slug odpovida, je velmi neefektivni. udelej si v databazi sloupecek slug

jAkErCZ
Člen | 321
+
0
-

David Matějka napsal(a):

a proc zas pracujes s parametrem id, kdyz pred tim bylo videt, ze mas v definici routy slug? pak se nediv, ze index neexistuje. prijde mi, ze vubec nevis, co se deje, a snazis se to vyresit nahodnyma upravama kodu. projdi si v klidu dokumentaci, pohledej na foru, kde se to hodnekrat resilo, koukni na nejaky videa z poslednich sobot, kde se routování probíralo…

a vytahnout si z databaze vsechny prispevky, abys zjistil, kde slug odpovida, je velmi neefektivni. udelej si v databazi sloupecek slug

Ale já reaguju na to co si psal ty… to znamená že já sem pracoval s $work_id tudíž vše jsem předalal na $work_id tak jak mám v db.

Viz
Router:

$router[] = new Route('[<locale=cs cs|en>/]<action>[/<work_id>]', [
    'presenter' => 'Front:Works',
    'action' => [
        Route::FILTER_STRICT => true,
        Route::FILTER_TABLE => [
            // řetězec v URL => akce presenteru
            'works' => 'default',
            'detail' => 'detail'
        ]
    ],
    NULL => [
        Route::FILTER_IN  => static function (array $parameters) use ($worksRepository): array {
            $parameters['work_id'] = array_search($parameters['work_id'], $worksRepository->getWorkBySlug());
            return $parameters;
        },
        Route::FILTER_OUT => static function (array $parameters) use ($worksRepository): array {
            bdump($worksRepository->getWorkBySlug());
            $parameters['work_id'] = $worksRepository->getWorkBySlug()[$parameters['work_id']];
            return $parameters;
        },
    ]
]);

Presenter

public function actionDetail(int $work_id){
    $work = $this->worksRepository->getWork($work_id);
    if ($work_id){
        $this->template->work = $work;
    }else{
        $this->redirect(':Front:Homepage:');
    }
}

DB:

work_id	int(11) Auto Increment
title	varchar(255)
code	varchar(255)
client	varchar(255)
url	varchar(255)
date	datetime
content	text
images_count	int(1)

A můj cíl je aby místo blbého ?work_id=1 mi to vrátilo název práce tudíž title
Ale když už jsem se dostal do fáze kdy mám funkci $worksRepository->getWorkBySlug()

Která dělá

/**
 * @return array
 */
public function getWorkBySlug ()
{
    return $this->database->table(self::TABLE_NAME)->fetchPairs('work_id','title');
}

Tak mám problém že to prostě nezná Undefined index: work_id přitom s ním pracuji a pokud si vrátím $parameters
tak mi to vrátí 2× arrey a to

action => "default"
presenter => "Front:Works"

a 2

work_id => 1
action => "detail"
presenter => "Front:Works"

Takže chyba bude nejspíše v tom že to vrátí 2 a jeden neobsahuje to work_id a druhý ano ale jak teď zajistit aby to v případě toho prvního kde chybí bylo třeba null aby to prostě fungovalo.

Snad sem to už napsal tak aby jste to pochopily.

Díky

David Matějka
Moderator | 6445
+
+1
-

ale jak teď zajistit aby to v případě toho prvního kde chybí bylo třeba null aby to prostě fungovalo.

to neumis napsat if (isset(..)) ? ale pokud to ta jedna (default) routa nevyzaduje, tak tu definici rozdel na dve – jednu pro default, ktera nema ten parametr work_id a druhou pro detail, ktera ten parametr ma

jAkErCZ
Člen | 321
+
0
-

David Matějka napsal(a):

ale jak teď zajistit aby to v případě toho prvního kde chybí bylo třeba null aby to prostě fungovalo.

to neumis napsat if (isset(..)) ? ale pokud to ta jedna (default) routa nevyzaduje, tak tu definici rozdel na dve – jednu pro default, ktera nema ten parametr work_id a druhou pro detail, ktera ten parametr ma

Bomba tohle funguje jen dotaz když si práce vracím

$this->database->table(self::TABLE_NAME)->fetchPairs('work_id','title');

Tak nějaký nápad jak na to implementovat webalize?

David Matějka
Moderator | 6445
+
+1
-

viz muj prispevek nahore

a vytahnout si z databaze vsechny prispevky, abys zjistil, kde slug odpovida, je velmi neefektivni. udelej si v databazi sloupecek slug

Kamil Valenta
Člen | 758
+
+1
-

Ukládej si v db title a zvlášť webalizovaný slug (na něm patrně i databázový index).