Jak odstraním specifický parametr z url?

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

Čau, nějakou dobu jsem se snažil vytvořil route který by mi místo ID zobrazoval název postu, bohužel se mi to nepodařilo nastavit pomocí routerů, ale pomocí předání dvou parametrů v n:href, ale aktuálně mám problém stím, že bych rád přebytečný parametr id z url vymazal.

  • Akutální stav: /name/23
  • Žádaný stav: /name

Četl jsem kompletní dokumentaci k Router, ale bohužel, jsem to nějak nepobral a menší postrčení by se mi hodilo. + jsem četl také jak už tento problém mnohokrát řešili, ale nejmladší topic byl 3roky starý a byly zde použité metody, které dnešní Nette již nemá.

n:href="Position:show $job->id, $job->url_anchor"
public function renderShow($id, $url_anchor)
{
    $job = $this->database->table('jobs_list')->get($id);

    if ( ! $job) {
       $this->error('Stránka nebyla nalezena');
    }

    $this->template->job = $job;
}

Aktuální router:

$router[] = new Route('/<url_anchor>/<id>', [
   'presenter' => 'Position',
   'action' => 'show',
   'id' => NULL
 ]);

Děkuji

Editoval Mardzis (25. 10. 2016 11:56)

Jan Mikeš
Člen | 771
+
0
-

Nekombinuj to, buď používej pouze id (a to překládej v routeru pomocí FILTER_IN + FILTER_OUT na název – to bych ti doporučoval, pokud si nevíš rady, mohu ti pomoci s implementací) nebo pouze slug (název). Pokud by jsi z routeru odstranil ID ale stejne ho používal v odkazu, tak se ti budou generovat url adresy ve tvaru neco/?id=123 takže se ho stejně nezbavíš.

Editoval Jan Mikeš (25. 10. 2016 12:07)

iNviNho
Člen | 352
+
0
-

Len doplním, že ak nechceš, aby sa ti ID zobrazovalo, tak ho nepredáš… Akonáhle ho ale nepredáš, tak metoda renderShow nedostane ID parameter a skončí to errorom.

Čiže ak ho potrebuješ = musíš ho predať. Je možnosť si ten ID parameter „skrášliť“ ako písal @JanMikeš :)

Mardzis
Člen | 33
+
0
-

Ok, tak nějak jsem si to myslel. Pokud mi dokážeš poslat nějaký aktuální téma, kde někdo něco podobného řešil tak bych to zkusil podle toho naimplementovat sám.

Jan Mikeš
Člen | 771
+
+1
-

Než hledat téma pro mě bude jednodušší ti poslat nástřel funkční implementace ;).
V routeru:

/** @var App\Routing\Sections */
private $sections; // inject pres konstruktor

$appRouter[] = new Route("<sectionId>/", [
	"presenter" => "Section",
	"action" => "detail",
	"sectionId" => [
		Route::FILTER_IN => [$this->sections, "sectionSlugToId"],
		Route::FILTER_OUT => [$this->sections, "sectionIdToSlug"],
	],
]);

App\Routing\Sections:

<?php

namespace App\Routing;

use Nette;
use App\Model\Entities\Section;
use Kdyby\Doctrine\EntityManager;

/**
 *  @author Jan Mikes <j.mikes@me.com>
 *  @copyright Jan Mikes - janmikes.cz
 */
final class Sections extends Nette\Object
{
	/** @var EntityManager */
	private $em;

	/** @var [] */
	private $sections = NULL;

	/** @var [] */
	private $sectionsString = NULL;


	public function __construct(EntityManager $em)
	{
		$this->em = $em;
	}

	public function sectionSlugToId($slug)
	{
		if ($this->sectionsString === NULL) {
			$this->setupSections();
		}

		return (empty($this->sectionsString[$slug]) ? NULL : $this->sectionsString[$slug]);

	}


	public function sectionIdToSlug($id)
	{
		if ($this->sections === NULL) {
			$this->setupSections();
		}

		return (empty($this->sections[$id]) ? NULL : $this->sections[$id]);
	}


	private function setupSections()
	{
		foreach ($this->em->getRepository(Section::class)->findAll() as $section) {
			$this->sections[$section->id] = $section->slug;
			$this->sectionsString[$section->slug] = $section->id;
		}
	}
}

Plně funkční ukázka, pracuje se sloupcem slug v tabulce (mám případně i implementace, kde se pracuje bez tohoto sloupce a adresa se generuje vždy za běhu).

Editoval Jan Mikeš (25. 10. 2016 13:06)

Mardzis
Člen | 33
+
0
-

Díky, nainstaloval jsem si potřebené knihovny co mi chyběly (Kdyby\Doctrine).

Kódu nejspíš rozumím, jen nevím. První snippet píšeš že by měl být umístěný v třídě RouterFactory přímo v funkci createRouter()? Píše mi to chybu Using $this when not in object context, což je logické protože ta třída je static. Co chápu špatně?

Jan Mikeš
Člen | 771
+
0
-

Tvůj router je totiž statická třída. Dej pryč static z jeho definice a registraci routeru v configu uprav takto (změn si případně název třídy):

services:
	- App\Routing\RouterFactory
	router: @App\Routing\RouterFactory::create
Mardzis
Člen | 33
+
0
-

Vyhazuje mi to chybu, zkoušel jsem promazat cache folder, ale

Method App\Routing\RouterFactory::createRouter() used in service 'routing.router' is not callable.

config.neon

services:
    - App\Routing\RouterFactory
    router: @App\Routing\RouterFactory::createRouter

RouterFactory.php

namespace App;

  use Nette;
  use Nette\Application\Routers\RouteList;
  use Nette\Application\Routers\Route;
  use Nette\Utils\Strings;


  class RouterFactory
  {
    /** @var App\Routing\Sections */
    private $sections;

    /**
     * @return Nette\Application\IRouter
     */
    public function createRouter()
    {
      $router = new RouteList;
      $router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default');
      $router[] = new Route("<id>/", [
        "presenter" => "Position",
        "action" => "show",
        "id" => [
          Route::FILTER_IN => [$this->sections, "sectionSlugToId"],
          Route::FILTER_OUT => [$this->sections, "sectionIdToSlug"],
        ],
      ]);
      return $router;
    }
  }
Jan Mikeš
Člen | 771
+
0
-

Máš chybně namespace. V configu máš App\Routing\RouterFactory, třídu máš ale v namespace App tedy App\RouterFactory

Ps. ještě ti chybí v routeru konstruktor kde si injectuješ přes DI třídu $services

Editoval Jan Mikeš (25. 10. 2016 18:15)

Jan Mikeš
Člen | 771
+
0
-

Další chybka, které jsem si všiml je, že na tu poslední routu se ti nikdy nedostane, protože před ní máš obecnou routu, která má přednost, prohoď jejich pořadí.

Editoval Jan Mikeš (25. 10. 2016 18:17)

Mardzis
Člen | 33
+
0
-

Route.php:339

call_user_func() expects parameter 1 to be a valid callback, first array member is not a valid class name or object
Jan Mikeš
Člen | 771
+
0
-

Už sis naplnil v RouterFactory property $sections? Protože je podle chybové hlášky NULL.

Mardzis
Člen | 33
+
0
-

Jo, já tam měl další stupidní chybu section místo sections

Ted mám tento error, ale to si ještě musím projít a zkontrolovat veškeré proměnné a jejich názvy abych nepoužíval ty z tvého příkladu:

Call to undefined method Container_bfd5a0f6a6::sectionIdToSlug()
Mardzis
Člen | 33
+
0
-

Nějak jsem pořád zaseklí na předchozí chybě, chyba je navázaná na n:href.

jiri.pudil
Nette Blogger | 1032
+
0
-

Vypadá to, že si do toho $sections předáváš celý Nette\DI\Container

Jan Mikeš
Člen | 771
+
0
-

Ukaž konstruktor v routerfactory a tvůj config kde jej registruješ + jak registruješ službu kterou chceš aby routerfactory obdržel.

Mardzis
Člen | 33
+
0
-

Ano mám tam celý Nette\DI\Container

function __construct(Nette\DI\Container $services)
{
   $this->sections = $services;
}

Editoval Mardzis (26. 10. 2016 13:22)

Jan Mikeš
Člen | 771
+
0
-

Potřebuješ pouze tu třídu, kde překládáš id na názvy a naopak.

Mardzis
Člen | 33
+
0
-

Změnil jsem construktor na:

function __construct(\App\Routing\Sections $services)
{
   $this->sections = $services;
}

a dostávám chybu

Service '67_App_RouterFactory': Service of type App\Routing\Sections needed by App\RouterFactory::__construct() not found. Did you register it in configuration file?

Je potřeba, dle chyby, upravit config, který jsem upravil následovně. Přidal jsem Sections: App\Routing je to takto správně?

services:
    - App\RouterFactory
    router: @App\RouterFactory::createRouter
    Sections: App\Routing
Jan Mikeš
Člen | 771
+
0
-

Těsně vedle ;). Máš chybu v zápise Sections: App\Routing
Sections: je pojmenování služby (může být anonymní a pojmenovávat nemusíš, pokud to vyloženě nepotřebuješ) a App\Routing je třída, která se má pro tuto službu použít. Správný zápis je tedy
buď

Sections: App\Routing\Sections

Nebo bez názvu služby

- App\Routing\Sections

Editoval Jan Mikeš (26. 10. 2016 17:21)

Mardzis
Člen | 33
+
0
-

Po čase sem se ktomu opět dostal, a ano teď už to „funguje“, jen dle tvého příkladu do nedokážu na mém zreplikovat.

Dostávám teď tuto chybu:

Metadata of class App\Model\Entities\Section was not found, because the class is missing or cannot be autoloaded.

Tak nějak okrajově chápu o čem to je, ale nedokážu to replikovat na moje řešení, hledám teď jednoduší řešení kde bych to zatím měl v mém Presenteru a až to lépe pochopím tak se pustím do podobného tomu tvému.

Díky

Mardzis
Člen | 33
+
0
-

@JanMikeš Celé jsem to tak nějak přepsal, abych měl oddělený admin a front, podle jistého návodu a ted řeším problém stále stím routováním které mi vůbec nejde do hlavy.

Aktuální RouterFactory.php

<?php

  namespace App;

  use Nette,
    Nette\Application\Routers\RouteList,
    Nette\Application\Routers\Route,
    Nette\Application\Routers\SimpleRouter;


  /**
   * Router factory.
   */
  class RouterFactory
  {

    /** @var \PostsRepository */
    private $postsRepository;


    function __construct(\PostsRepository $postsRepository)
    {
      $this->postsRepository = $postsRepository;
    }

    /**
     * @return \Nette\Application\IRouter
     */
    public function createRouter()
    {
      $router = new RouteList();

      // Admin
      $router[] = new Route(
        'admin/<presenter>/<action>/<id>', [
          'module'    => 'Admin',
          'presenter' => 'Admin',
          'action'    => 'default',
          'id'        => NULL,
        ]
      );

      // Front

      $router[] = new Route(
        '<presenter>/<id>', [
          'module'    => 'Front',
          'presenter' => 'Post',
          'action'    => 'show',
          'id'        => [
            Route::FILTER_IN  => function($url) {
              return $url;
            },
            Route::FILTER_OUT => function($id) {
              return $this->postsRepository->getSlugById($id);
            }
          ]
        ]
      );

      $router[] = new Route(
        '<presenter>/<action>/<id>', [
          'module'    => 'Front',
          'presenter' => 'Homepage',
          'action'    => 'default',
          'id'        => NULL,
        ]
      );

      return $router;
    }

  }

Dotazy na id a slug

public function getSlugById($id)
    {
      $this->connection->table('posts')
        ->where('id = ?', $id)
        ->fetch()->slug;
    }

    public function getIdBySlug($slug)
    {
      $this->connection->table('posts')
        ->where('slug = ?', $slug)
        ->fetch()->id;
    }

Přepis ID by měl vracet $slug, ale bohužel nic se neděje, mám defaultní /post/show/3. Co mi nedochází?

Jan Mikeš
Člen | 771
+
0
-

Máš možnost si nainstalovat xdebug a používáš IDE například PhpStorm nebo Netbeans?
Velmi bych doporučil si to odkrokovat, zjistit jaké parametry funkce dostáva, jestli se vůbec volá atd.
Pokud debugger nepoužíváš, opravdu doporučuji aby sis to vyzkoušel, věnoval instalaci a naučení se s ním třeba jedno odpoledne, výsledky a zlepšení práce pocítíš téměř okamžitě.

CZechBoY
Člen | 3608
+
0
-

@JanMikeš Kdyby jedno odpoledne :D
Pokud to chceš k jedný funkci tak si myslim že celkem stačí dump (pokud teda nemáš hodně času hrát si s debuggerem nebo si trošku zkušenější syadmin než já).

Mardzis
Člen | 33
+
0
-

Super, díky za info. Xdebug jsem dodnes neznal a pomohlo mi to vyřešit můj problém. Vše už funguje skvěle.