Generování do formulářového selectu z databáze

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

Zdravím,
mám takový asi dost elementární dotaz, ale jelikož nejsem s Nette ještě dostatečný kamarád, ptám se pro urychlení zde :).
Jde mi o to, že potřebuji do klasického formulářového selectu vypsat záznamy z nějaké databázové tabulky.

    $jmena = array(
 '1' => 'Lenka',
		'2' => 'Petra',
		'3' => 'Jana',
 );
$form->addSelect('jmeno', 'Jméno:', $jmena)->setPrompt('Zvolte jméno');

Potřebují mít tady v proměnné $jmena pole dat, která jsou ovšem vypsaná přímo z databáze.

V modelu bych vytvořil třeba:

public function VypisJmena(){
    $this->db->query("SELECT id, jmeno FROM jmena_zen ORDER BY jmeno ASC");

   }

No a teď netuším, jak naplnit to pole a jak ho zavolat v presenteru…
Předem díky za rady.

Editoval Dark0ne (4. 5. 2015 19:01)

Mysteria
Člen | 797
+
+1
-

Tohle ti vrátí přesně to pole, který potřebuješ:

public function getFemaleNames() {
	return $this->db->table('jmena_zen')->order('jmeno')->fetchPairs('id', 'jmeno');
}

A jak ho zavolat v presenteru?
Normálně ho do presenteru injectneš pomocí anotace a pak ho použiješ:

/** @var SomeModel @inject */
public $someModel;

$form->addSelect('jmeno', 'Jméno:', $this->someModel->getFemaleNames())->setPrompt('Zvolte jméno');
Dark0ne
Člen | 47
+
0
-

Děkuji za radu, tuším, jak by to mělo fungovat, ovšem stále mi to hází chybu

Nette\MemberAccessException

Call to undefined method Nette\Database\Connection::table().

Tedy pravděpodobně musím někde zprovoznit metodu table(), pokud to dobře chápu?

Oli
Člen | 1215
+
0
-

Je to proto, ze $this->db musí byt context, ne connection.

Dark0ne
Člen | 47
+
0
-

A můžu se zeptat, jak to změnit?

Dark0ne
Člen | 47
+
0
-

Modelový soubor vypadá takhle (jedná se o fci getDum):

<?php

namespace App\Model;

use Nette,
	Nette\Utils\Strings,
	Nette\Security\Passwords,
        Nette\Database\Context;


/**a
 * Users management.
 */
class BytyModel extends Nette\Object
{
  private $db=null;


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

public function getByty(){

      $stmt=$this->db->query("SELECT * FROM byty");
      return $stmt->fetchAll();
  }



 public function getDum() {


    return $this->db->table('domy')->order('id_domy')->fetchPairs('id_domy', 'ulice');
}


}
Zax
Člen | 370
+
0
-

V konstruktoru si vyžádáš Nette\Database\Context místo Nette\Database\Connection.

Oli
Člen | 1215
+
0
-

Předpokládám, že v configu máš něco jako - BytyModel(@connection). Tak oddělej celý závorky a v konstruktoru si typehintem vyžádej Context, jak píše @Zax.

Pokud nepředáváš parametry (string, integer, array, …), ale nějakou třídu nebo interface, tak to v configu vubec nemusíš uvádět, napiš to do constructoru a on si to vytáhne. Navíc ti to bude napovídat, jaký metody ta třída má.

Dark0ne
Člen | 47
+
0
-

Jsem z toho teď nějaký zmatený, mohl bys to prosím uvést na konkrétním příkladě? Omlouvám se, ale zatím se v tom, tak dobře nevyznám.
V config.neon mám pouze něco takového

services:
	- App\Model\UserManager
	- App\Forms\SignFormFactory
	router: App\RouterFactory::createRouter
	database: @database.default
Oli
Člen | 1215
+
+1
-

Tak to jsem teď já zmatenej z tebe :-) Jak tam dostaneš tu třídu BytyModel, když ji nemáš v configu? Vytváříš ji pomocí new BytyModel? To právě vubec nemusíš. Udělal bych to nějak takhle:

services:
	database: @Nette\Database\Context
	- App\Model\UserManager
    - App\Forms\SignFormFactory
    router: App\RouterFactory::createRouter
	- BytyModel

BytyModel

class BytyModel extends Nette\Object
{
 	private $db;

	public function __construct(\Nette\Database\Context $db) {
		$this->db = $db;
	}

	public function getByty(){
		return $this->db->table('byty');
		//$stmt=$this->db->query("SELECT * FROM byty");
		//return $stmt->fetchAll();
	}
}

Presenter

class MyPresenter extends BasePresenter
{

	private $bytyModel;

	public function __construct(BytyModel $bytyModel)
	{
		$this->bytyModel = $bytyModel
	}

	public function createComponentForm()
	{
		$form = new Form;

		$form->addSelect('flats', 'Byty', $this->bytyModel->getByty()->fetchPairs('id', 'name'))
			->setPromt('--- Vyberte ---');

		return $form;
	}

}

Ještě lepší je si na ten formulář udělat jako komponentu, ale do toho bych teď nezabředával ;-)

Editoval Oli (5. 5. 2015 12:11)

Dark0ne
Člen | 47
+
0
-

No :D, já to v Presenteru mám takhle

<?php

namespace App\Presenters;

use Nette,
	App\Model,
        Nette\Application\UI\Form;


/**
 * Homepage presenter.
 */
class BytyPresenter extends BasePresenter
{
        private $model=null;
        private $data = null; //pridana promena pro edit

        private function getModel(){
            if(!isset($this->model)){

                $this->model=new Model\BytyModel($this->context->getService("database"));

            }

            return $this->model;
        }
        public function renderDefault()
	{

		$this->template->byty = $this->getModel()->getByty();

	}

A pak se v něm odkazuju $this->getModel()->Metoda()…

Vím, že jsem asi otravnej, ale nešlo by vyřešit ten můj problém způsobem, který by šel napasovat na tuto variantu řešení ?(Mám už tímto způsobem napsánou velkou část aplikace a potřebuji to mít během krátké doby zprovozněné).
Děkuji všem za rady a trpělivost.

David Matějka
Moderator | 6445
+
+1
-

pokud nepouzivas NDBT (tedy „magicke“ table), ale pises pouze sql, i tak muzes pouzit fetchPairs, aniz bys menil zavislosti:

public function getDum() {
    return $this->db->query("SELECT id_domy, ulice FROM domy ORDER BY id_domy")->fetchPairs("id_domy", "ulice");
}
Dark0ne
Člen | 47
+
0
-

Paráda! Děkuji moc za rady :).

Oli
Člen | 1215
+
+1
-

@Dark0ne dám ti pár snad dobrých rad do začátku :-)

  1. Využívej DI (dependency injection). Má to hned 3 přínosy (má jich víc, tyhle mě napadly na první dobrou)
    1. Nemusíš se starat o závislosti takto vložených závislostí (doplní se samy)
    2. Zahlásí ti to chybu, pokud se budeš snažit vytvořit objekt, který potřebuje ke své funkčnosti něco co nemůže najít. Takže pokud by jsi chtěl třeba vytvořit presenter, který potřebuje FlatModel, ale nepředal mu ho, tak to spadne. Pokud by jsi to vytvářel tím new operátorem, tak ti to spadne až ve chvíli, kdy se dostaneš na action, ve které tu třídu vytváříš. ⇒ dřív odhalíš chybu v kódu.
    3. Přehledně na jednom místě vidíš všechny závislosti, které třída má. Představ si, že píšeš třídu, která má 6 závislostí a všechny vytváříš pomocí new. A teď tu třídu chceš použít v jiným projektu. Asi víš kam mířím…
  2. Nepoužívej $this->context! Vytváří to skryté závislosti viz bod 1.

Ten tvůj presenter by šel přepsat velice jednoduše například takhle:

namespace App\Presenters;

use Nette,
    App\Model,
        Nette\Application\UI\Form;


/**
 * Homepage presenter.
 */
class BytyPresenter extends BasePresenter
{
	private $bytyModel;
	private $data = null; //pridana promena pro edit

	public function __construct(Model\BytyModel $bytyModel)
	{
		$bytyModel = $bytyModel;
	}

	public function renderDefault()
	{
		$this->template->byty = $this->bytyModel->getByty();
	}

	// ...

}

Řekl bych že to je výrazně kratší a přehlednější a o to v podstatě jde :-)

Editoval Oli (5. 5. 2015 23:02)