Nette database explorer – joinovani tabulek

TMScz
Člen | 24
+
0
-

Měl bych jeden takový dotaz…

Chci používat Nette Database Explorer místo Dibi, ale řeším vazby mezi tabulkama (ano, vím že se to na forech resi ale ja reseni proste nenasel)…

Pro příklad:
DB (osekane):

tabulka „books“, sloupce id, title, created_by
tabulka „users“, sloupce id, name

vazba mezi tabulkama je books.created_by > users.id

Mám Model, ve kterém mám fci na vypis knih

public function selectAll() {
	return $this->db->table('books');
}

cekal jsem, ze kdyz budu chtit ve vypise i info o tom kdo ji pridal, udelam to nasledovne

public function selectAll() {
	return $this->db->table('books')->select('books.*')->select('users(created_by).name');
}

ale tohle mi padne na vyjiku No reference found for $books->users

ja uz tady na foru podobne dotaze nasel ale proste mi to nefunguje a uz nevim co delam spatne… ale urcite to bude nejaka pitomost…

Za každé nakopnutí budu moc rád, díky

Editoval TMScz (11. 9. 2018 12:03)

Ondřej Kubíček
Člen | 494
+
+3
-

musel bys ten sloupce mít pojmenovaný jako users_id pak můžeš udělat >users, takhle to musíš ziskat přes ref

public function selectAll() {
   return $this->db->table('books');
}

...

foreach ($this->selectAll() as $book) {
   echo "book author: " . $book->ref('created_by')->name;
}
TMScz
Člen | 24
+
0
-

ja na tomhle prave narazil… ano, takhle to co potrebuji vypisu, „ale“… ted to napisu asi ne uplne spravne, ale kdyz mam appku kde mam modely, presentery a views, tak pokud mam db dotazy v modelu, tak to potrebuju poslat do presenteru a dal. jake je tedy „spravne“ reseni? ulozit si to v modelu do nejake promenne, pomoci ->ref() si ty data donacist a pak to komplet „vratit“ presenteru, nebo mit v modelu jen ten zakladni dotaz, a v presenteru to pak dal upravovat podle potreby (donacitat data)?

Omlouvam se jestli se ptam blbe ale trochu se v tom motam. Ale jinak to co si popisoval je mi jasne, za to diky…

GEpic
Člen | 566
+
+2
-

Jisté postupy tu jsou (a na fóru je najdeš – je to spíš filozofické téma), ale je to víceméně na tobě (a v závislosti na tom jak se budeš vyvíjet). Pokud si budeš surová data z modelu tahat do presenteru a upravovat si je tam, a později je budeš chtít ve stejném formátu vypsat i jinde a budeš muset duplikovat kód, tak sám dojdeš k tomu, že by s tím mělo jít něco udělat. Buďto ti bude model vracet data přesně v požadovaném formátu a presenter je jen předá do šablony, nebo si mezi to vložíš jinou vrstvu – např. pokud bude potřeba pracovat s více modely naráz (a nebo použiješ nějaké ORM). Je možné mezi to vložit např. i nějakou službu (service), pokud např. navíc potřebuješ k datům z databáze dotáhnout např. nějaká data z API (např. k IČU data z aresu, pokud nejsou uložené, nebo např. k vozidlům aktuální GPS polohu z API, atp.).

Editoval GEpic (11. 9. 2018 14:17)

TMScz
Člen | 24
+
0
-

Tak diky za postrehy… kdyz vezmu trochu jiny priklad a budu tedy pracovat s tim ze z modelu chci hotova data do presenteru, tak by to mohlo byt priblizne takto… ?

public function my(int $user_id): array {
    $query = $this->database->table('pr_articles')->where('assigned_to', $user_id);

    $articles = [];
    foreach ($query as $article) {
      array_push($articles, ['article_id' => $article->id,
                                 'client' => $article->client,
                               'state_id' => $article->state_id,
                             'state_name' => $article->ref('state_id')->label,
                              'dealer_id' => $article->dealer_id,
                            'dealer_name' => $article->ref('dealer_id')->name,
      ]);
    }

    return $articles;
  }

K zakladnimu vysledku si pomoci ref() (popr. related()) doplnim potrebne udaje z dalsich tabulek a vysledek poslu do sveta… Ten dotaz je funkcni, jen se chci ujistit, ze jsem to pochopil spravne :)

a jeste jednou diky za tipy ;)

Editoval TMScz (11. 9. 2018 15:26)

CZechBoY
Člen | 3608
+
0
-

jo, tak většinou se to neukládá do čistýho pole ale do nějaký přepravky – vytvoříš třídu speciálně na tento výsledek a ten potom používáš jinde.

TMScz
Člen | 24
+
0
-

Můžeš to trochu upřesnit pls? Třeba na tom mém příkladě? Tuším jak to s tou „přepravkou:)“ myslíš, ale ať mám jistotu… nechci se tu pak blbě ptát na jasné věci…

CZechBoY napsal(a):

jo, tak většinou se to neukládá do čistýho pole ale do nějaký přepravky – vytvoříš třídu speciálně na tento výsledek a ten potom používáš jinde.

Kcko
Člen | 468
+
0
-

CZechBoY napsal(a):

jo, tak většinou se to neukládá do čistýho pole ale do nějaký přepravky – vytvoříš třídu speciálně na tento výsledek a ten potom používáš jinde.

A na raw poli je špatné co?

CZechBoY
Člen | 3608
+
+2
-

@Kcko Tak pokud to použiješ jinde než v šabloně tak ti IDE napoví co tam máš…

@TMScz

public function my(int $user_id): array
{
    $query = $this->database->table('pr_articles')->where('assigned_to', $user_id);

    $articles = [];
    foreach ($query as $article) {
        array_push(
            $articles,
            new Article(
                $article->id,
                $article->client,
                new State(
                    $article->state_id,
                    $article->ref('state_id')->label
                ),
                new Dealer(
                    $article->dealer_id,
                    $article->ref('dealer_id')->name
                )
            )
        );
    }

    return $articles;
}

class Article
{
    public function __construct(int $id, int $client, State $state, Dealer $dealer) { ... }

    public function getID(): int {...}
    public function getState(): State {...}
}
class State
{
    public function __construct(int $id, string $name) { ... }

    public function getID(): int {...}
    public function getName(): string {...}
}
TMScz
Člen | 24
+
0
-

@CZechBoY Díky, zkouknu to :)

TMScz
Člen | 24
+
0
-

@CZechBoY mohu na Tebe jeste dotaz? trochu jsem si s tim hral tak jak jsi ukazoval… musim rict ze me trochu mrzi ze jsem to takhle neresil o par webu driv :)) jen jsem trochu tapal jak tam pridat related vazby typu „jeden user ma vice roli“. V dokumentaci to je hezky popsane, ale pro jistotu, jestli mohu poprosit o info jestli jdu spravnym smerem…

<?php

namespace App\Model;

use Nette;
use Nette\Utils;

class UsersManagement {

  /** @var Nette\Database\Context */
  private $db;

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

  public function selectAll()
  {
    // nactu vsechny uzivatele
    $query = $this->db->table('users')
                      ->select('id, name, surname, email, archived')
                      ->order('surname');

    // vytvorim si pole uzivatelu
    $users = [];
    foreach ($query as $user)
    {
      // zjistim datum posledniho prihlaseni
      $logged_at = $this->db->table('users_login_history')
                            ->select('logged_at')
                            ->where('user_id', $user->id)
                            ->order('logged_at DESC')
                            ->limit(1)->fetch();

      // vytvorim noveho usera
      $u = new User(
        $user->id,
        $user->name,
        $user->surname,
        $user->email,
        $user->archived,
        $logged_at['logged_at']
      );


      // ke kazdemu uzivateli pridam seznam jeho roli
      foreach ($user->related('users_roles_assoc') as $role)
      {
        $u->addRole(
          new Role(
            $role->role_id,
            $role->ref('role_id')->name,
            $role->ref('role_id')->label
          )
        );
      }

      array_push($users, $u);
    }

    return $users;
  }

  public function select($id) {
    return $this->db->table('users')->get($id);
  }

}

/**
 * User Class
 */
class User
{
  public $id;
  public $name;
  public $surname;
  public $email;
  public $archived;
  public $last_logged_at;
  public $roles = [];

  function __construct(int $id, string $name = NULL, string $surname = NULL, string $email, bool $archived, \DateTime $last_logged_at = NULL)
  {
    $this->id = $id;
    $this->name = $name;
    $this->surname = $surname;
    $this->email = $email;
    $this->archived = $archived;
    $this->last_logged_at = $last_logged_at;
  }

  public function addRole(Role $role): User
  {
    $this->roles[] = $role;
    return $this;
  }
}

/**
 * User Roles
 */
class Role
{
  public $id;
  public $name;
  public $label;

  function __construct(int $id, string $name, string $label)
  {
    $this->id = $id;
    $this->name = $name;
    $this->label = $label;
  }

}

cim si ale nejsem uplne jisty je ta cast

// zjistim datum posledniho prihlaseni
      $logged_at = $this->db->table('users_login_history')
                            ->select('logged_at')
                            ->where('user_id', $user->id)
                            ->order('logged_at DESC')
                            ->limit(1)->fetch();

je to mozne zase udelat nejak pres tu related vazbu? je tam potreba vzit nejnovejsi, resp. posledni zaznam z tabulky pro konkretniho usera

predem diky za pripadne pripominky…

CZechBoY
Člen | 3608
+
0
-

@TMScz
Ten logged_at bych asi řešil přes subquery. Úplně přesně nevim jak by to mělo vypadat, ale můžeš zkusit třeba

$query = $this->db->table('users')
              ->select('id, name, surname, email, archived')
              ->select('(SELECT logged_at FROM users_login_history WHERE user_id=users.user_id ORDER BY logged_at DESC)')
              ->order('surname');

Což má teda nevýhodu, že píšeš custom sql v query builderu.

Jinak ještě k té metodě selectAll – radši bych instancioval třídu User s rolema rovnou než je tam takhle přidával. Na to by mělo stačit přehodit ty dva bloky.

  $roles = [];

  // ke kazdemu uzivateli pridam seznam jeho roli
  foreach ($user->related('users_roles_assoc') as $role)
  {
      $roles[] = new Role(
        $role->role_id,
        $role->ref('role_id')->name,
        $role->ref('role_id')->label
      )
    );
  }

  // vytvorim noveho usera
  $u = new User(
    $user->id,
    $user->name,
    $user->surname,
    $user->email,
    $user->archived,
    $logged_at['logged_at'],
    $roles
  );

  array_push($users, $u);
}

btw proč ti metoda select vrací ActiveRow a ne taky tu přepravku jako v případě selectAll?

Kcko
Člen | 468
+
0
-

@CZechBoY díky za vysvětlení

TMScz
Člen | 24
+
0
-

@CZechBoY select v te ukazce byt nemel, to je zapomenute a uz upravene:)

Diky za postrehy!

CZechBoY napsal(a):

btw proč ti metoda select vrací ActiveRow a ne taky tu přepravku jako v případě selectAll?

Editoval TMScz (20. 9. 2018 20:05)

TMScz
Člen | 24
+
0
-

Jeste me k tomu napadla jedna otazka, kdyz data nebudu vypisovat ale ukladat.. Dejme tomu ze trida Article ma metodu save(), ktera ma poslat data do db. V te tride tedy musim mit pristup k db, takze v kontruktoru bude napr.:

public function __construct(Nette\Database\Connection $database, int $id, string $name, ......)
    {
        $this->database = $database;
        $this->id = $id;
        $this->name = $name;
        .........
    }

V presenteru mam data z formulare, vytvorim si novy objekt, nacpu do nej data a dam ulozit. A co me zajima, jestli tedy se opravdu pri kazdem vytvoreni instance te tridy je nutne tam posilat i db_connection? Takze naco jako

$article = new Article(
	$db_connection,
	$values['...'],
	$values['...'],
	......
);

$article->save();

Smysl to dava, ale treba to mam cele blbe navrzene a jde to resit jinak… snazil jsem se to udelat prehledne a pekne se v tom motam :D
Treba je reseni v kontruktoru nechat jen to db spojeni, pak tedy vytvorit „prazdny“ objekt s a teprve po vytvoreni mu zacit prirazovat hodnoty? Vlastne tak jak to je v dokumentaci

Jdu na to uplne blbe, nebo jsem se jen nekde seknul?

Diky za nazory…

Mysteria
Člen | 797
+
+3
-

@TMScz: Osobně nevidím důvod proč by nějaká třída Article, která je „jenom“ přepravkou na data měla mít zodpovědnost i za své ukládání. To by měl dělat nějaký manager. Takže osobně bych to řešil nějak takto:

class Article {
	private $id;
	private $title;
	private $content;

	public function __construct($id, $title, $content) {
		$this->id = $id;
		$this->title = $title;
		$this->content = $content;
	}

	public function getId() {
		return $this->id;
	}

	public function toArray() {
		return [
			'title' => $this->title,
			'content' => $this->content,
		];
	}
}

class ArticleManager {
	/** @var \Nette\Database\Context */
	private $database;

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

	public function create(Article $article) {
		return $this->database->table('article')->insert($article->toArray());
	}

	public function update(Article $article) {
		return $this->database->table('article')->where($article->getId())->update($article->toArray());
	}

	...

}

Editoval Mysteria (10. 10. 2018 19:38)