Přístup k více modelům v jednom presenteru, přístup k db odkudkoli, problém s backjoin

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

Jak se dá přistoupit ke 2 a více modelům v 1 presenteru?

Pro připojení jednoho napíši do presenteru funkci a mám vystaráno:

<?php
	public function __construct(Model\Products $products) {
		$this->products = $products;
	}
?>

Někdo mi poradil, abych si udělal model jako službu v config.neon:

<script>
services:
		orderService: Orders(@nette.database.default)
</script>

Kde Orders mám

<?php
class Orders extends \Nette\Object {
	/** @var Nette\Database\Context */
	private $database;

	public function __construct(Nette\Database\Context $database) {
		$this->database = $database;
	}
// a dalsi vlastni metody
}
?>

což mi ale vyhazuje Nette\DI\ServiceCreationException „Class Orders used in service ‚orderService‘ not found or is not instantiable“.

Dále jsem narazil když jsem si definoval vlastní routy podle videa J. Smitky o Routování a přístupu do databáze. Mám vytvořené IN a OUT filtry, ale nevím jak si vrátit název produktu z db, abych si mohl zobrazit url jako url/produkty/nazev-produktu místo url/produkty/id. Problém mám zatím jenom se získáním názvu z db. Routy nastavuji v bootstrap.php a nevím, jak se tam dostanu k modelu produktů, který mám napsaný (Základní struktura je stejná jak u Orders výš).

A nakonec když převedu svůj problém do modelu v dokumentaci tak se mi nedaří vypsat všechny tagy, které patří k jedné knize. Můj současný kód je:

<?php
		$books = $this->database->table('book');
		return $books->where(':book_tag.tag.name', $cat);
?>

Kód výš mi vyhazuje sql chybovou hlášku"SQLSTATE[42P01]: Undefined table: 7 ERROR: relation „product“ does not exist LINE 21: AND c.oid = ‚„product“‘::regclass ^".

ali
Člen | 342
+
+1
-

James07 napsal(a):

Jak se dá přistoupit ke 2 a více modelům v 1 presenteru?

Pro připojení jednoho napíši do presenteru funkci a mám vystaráno:

<?php
	public function __construct(Model\Products $products) {
		$this->products = $products;
	}
?>
public function __construct(Model\Model_1 $model1,Model\Model_2 $model2,Model\Model_3 $model3) {
	...
}

U registrace modelu v konfigu musis zadavat i namespace

services:
        orderService: Namespace\Orders(@nette.database.default)
James07
Člen | 41
+
0
-

Děkuji za pomoc, předat více parametrů v __construct jsem zkoušel i předtím, ale myslel jsem si, že se nic nestalo. Měl jsem v modelu vložení do jiného schématu databáze, nyní jsem opravil schéma a funguje to. Když přidám namespace k Orders tak mi to začne hlásit chybu „Service ‚orderService‘: Reference to missing service ‚nette.database.default‘“. Svou databázi mám deklarovanou v config.local.neon přes database: dsn, user, …

David Matějka
Moderator | 6445
+
+3
-

@James07
pokud konfigurujes databazi primo pres sekci database (a nikoliv jiz pod nette), nebude jiz sluzba prefixovana tim nette., takze to bude jen database.default. Ale pozor, database.default odkazuje na Connection, nikoliv na Context. K tomu se dostans pres database.default.context.

Pokud vsak pouzivas pouze jednu databazi, je uplne zbytecne to v neonu uvadet (uvedes pouze nazev tridy bez argumentu) – postara se o to autowiring.

James07
Člen | 41
+
0
-

Tak už můžu přidávat n modelů do 1 presenteru, používat modely jako služby. Z původního dotazu zbývá poslední dotaz:

A nakonec když převedu svůj poslední problém do modelu v dokumentaci tak se mi nedaří vypsat všechny knihy, které patří k jednomu tagu. Můj současný kód je:

<?php
        $books = $this->database->table('book');
        return $books->where(':book_tag.tag.name', $tagName);
?>

Kód výš mi vyhazuje sql chybovou hlášku"SQLSTATE[42P01]: Undefined table: 7 ERROR: relation “product” does not exist LINE 21: AND c.oid = ‘“product”’::regclass ^".

Editoval James07 (18. 2. 2015 14:17)

David Matějka
Moderator | 6445
+
0
-
  1. ta chybova hlaska bude patrne k necemu jinemu, nez ke kodu, co uvadis
  2. ten tvuj kod nedava smysl, tim tvym by ses zeptal "vyber vsechny knizky, ktere maji tag s name = $id. na to, co potrebujes – tedy vybrat tagy dle knizky, jsou dve moznosti
  1. related
$book = $this->database->table('book')->get($id);
foreach ($book->related('book_tag') as $book_tag) {
	echo $book_tag->tag->name;
}
  1. primo vybrat tagy jednim dotazem
$tags = $this->database->table('tag')->where(':book_tag.book.id', $id);
James07
Člen | 41
+
0
-

Jsem to špatně popsal. To mělo vypsat všechny knihy z dané kategorie. $id je název kategorie, takže např. „PHP“.

Ekvivalent k :
SELECT * FROM book
JOIN book_tag ON book.id = book_tag.book_id
JOIN tag ON tag.id = book_tag.tag_id
WHERE tag.name = $cat

David Matějka
Moderator | 6445
+
0
-

V tom pripade by melo fungovat to, co pises. Ale jak rikam – ta chybova hlaska patrne souvisi s necim jinym…

James07
Člen | 41
+
0
-

Založil jsem novou databázi, udělal vše odznovu a nyní mi to vyhazuje tuto nette chybu:

Nette\Database\Reflection\MissingReferenceException

No reference found for $nette.book->related(book_tag)

ale db mám takto:
book
id pk

tag
id pk

book_tag
pk(book_id + tag_id)
book_id FK na id v book
tag_id FK na id v tag

Editoval James07 (18. 2. 2015 18:48)

Томас
Člen | 85
+
-4
-

Jen se chci zeptat

public function __construct(Model\Model_1 $model1,Model\Model_2 $model2,Model\Model_3 $model3) {
    ...
}

To je normální? Však to je hrozný. Neni lepší si vytvářet instance až když je potřebuju?

class cc
{

	private $mm = NULL;

 	public function __construct(Context $db){
		parent::__construct($db);
		$this->mm = new MMModel($db);
	}

	public function renderNevim()
	{
		$model = new Model($this->db);
		$this->template->neco = $model->foo();
		$this->template->xx = $this->mm->foo();
	}

	public function renderNevim2()
	{
		$this->template->xx2 = $this->mm->foo2();
	}
}

// popr jen

public function __construct(Context $db){
	$this->neco = new Model1();
	$this->neco2 = new Model2();
}

Stejně se ty proměnný musejí někam uložit, tak aspoň nebude ten konstruktor tak hnusnej.

Caine
Člen | 216
+
0
-

Jeste je moznost si v presenterech predavat modely viz https://doc.nette.org/…dependencies#…

David Matějka
Moderator | 6445
+
0
-

@James07 divny… posli sem schema db + presne kod, co spoustis

@Томас

Neni lepší si vytvářet instance až když je potřebuju?

neni. Je principem DI si o zavislosti pozadat a nevytvaret je. Co bys delal, kdybys najednou v Model potreboval napriklad cache storage? Injectnul cache storage do vsech presenteru a zmenil zpusob vytvareni?

Томас
Člen | 85
+
0
-

@Томас

Neni lepší si vytvářet instance až když je potřebuju?

neni. Je principem DI si o zavislosti pozadat a nevytvaret je. Co bys delal, kdybys najednou v Model potreboval napriklad cache storage? Injectnul cache storage do vsech presenteru a zmenil zpusob vytvareni?

Ok, už nějakej ten pátek dělám PHP a stále mám ještě zlozvyk z C++ kde bych za instanci navíc dostal facku. Aspoň, že už neunsetuju všechny privátní instance v destruktorech. :-D

duke
Člen | 650
+
0
-

Neni lepší si vytvářet instance až když je potřebuju?

Záleží čeho je to instance. Pokud je s vytvořením instance spojena větší režie, pak ano. Ale ani v takovém případě nemusíš vytvářet instance přímo, nýbrž si v duchu DI můžeš nechat předat službu, která ti tu instanci na požádání dodá. S vytvářením takové služby už žádná větší režie není.

Томас
Člen | 85
+
0
-

duke napsal(a):

Neni lepší si vytvářet instance až když je potřebuju?

Záleží čeho je to instance. Pokud je s vytvořením instance spojena větší režie, pak ano. Ale ani v takovém případě nemusíš vytvářet instance přímo, nýbrž si v duchu DI můžeš nechat předat službu, která ti tu instanci na požádání dodá. S vytvářením takové služby už žádná větší režie není.

Kk, díky

James07
Člen | 41
+
0
-

@matej21

<?php
// presenter:
$this->template->products = $this->context->productsService->getProductsFromCategory($name);
// model:
	/** @return Nette\Database\Table\ActiveRow */
	public function getProductsFromCategory($cat) {
		return  $this->database->table('nette.product')->where(':category_product.category.name', $cat);
    }
?>

Tabulka category

– PostgreSQL database dump
--

-- Dumped from database version 9.3.5
– Dumped by pg_dump version 9.3.5
– Started on 2015–02–19 06:24:18

SET statement_timeout = 0;
SET lock_timeout = 0;
SET client_encoding = ‚UTF8‘;
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;

SET search_path = nette, pg_catalog;

SET default_tablespace = '';

SET default_with_oids = false;

--
– TOC entry 171 (class 1259 OID 25237)
– Name: category; Type: TABLE; Schema: nette; Owner: james; Tablespace:
--

CREATE TABLE category (
id integer NOT NULL,
name character varying(25) NOT NULL
);

ALTER TABLE nette.category OWNER TO james;

--
– TOC entry 172 (class 1259 OID 25240)
– Name: categories_id_seq; Type: SEQUENCE; Schema: nette; Owner: james
--

CREATE SEQUENCE categories_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER TABLE nette.categories_id_seq OWNER TO james;

--
– TOC entry 1960 (class 0 OID 0)
– Dependencies: 172
– Name: categories_id_seq; Type: SEQUENCE OWNED BY; Schema: nette; Owner: james
--

ALTER SEQUENCE categories_id_seq OWNED BY category.id;

--
– TOC entry 1842 (class 2604 OID 25269)
– Name: id; Type: DEFAULT; Schema: nette; Owner: james
--

ALTER TABLE ONLY category ALTER COLUMN id SET DEFAULT nextval(‚categories_id_seq‘::regclass);

--
– TOC entry 1961 (class 0 OID 0)
– Dependencies: 172
– Name: categories_id_seq; Type: SEQUENCE SET; Schema: nette; Owner: james
--

SELECT pg_catalog.setval(‚categories_id_seq‘, 1, false);

--
– TOC entry 1954 (class 0 OID 25237)
– Dependencies: 171
– Data for Name: category; Type: TABLE DATA; Schema: nette; Owner: james
--

COPY category (id, name) FROM stdin;
1 Kávy
2 Čaje
4 Dezerty
5 Zmrzliny
3 Alkohol
\.

--
– TOC entry 1844 (class 2606 OID 25274)
– Name: categories_name_key; Type: CONSTRAINT; Schema: nette; Owner: james; Tablespace:
--

ALTER TABLE ONLY category
ADD CONSTRAINT categories_name_key UNIQUE (name);

--
– TOC entry 1846 (class 2606 OID 25276)
– Name: categories_pkey; Type: CONSTRAINT; Schema: nette; Owner: james; Tablespace:
--

ALTER TABLE ONLY category
ADD CONSTRAINT categories_pkey PRIMARY KEY (id);

-- Completed on 2015–02–19 06:24:18

--
– PostgreSQL database dump complete
--

Tabulka category_product

– PostgreSQL database dump
--

-- Dumped from database version 9.3.5
– Dumped by pg_dump version 9.3.5
– Started on 2015–02–19 06:24:46

SET statement_timeout = 0;
SET lock_timeout = 0;
SET client_encoding = ‚UTF8‘;
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;

SET search_path = nette, pg_catalog;

SET default_tablespace = '';

SET default_with_oids = false;

--
– TOC entry 173 (class 1259 OID 25242)
– Name: category_product; Type: TABLE; Schema: nette; Owner: james; Tablespace:
--

CREATE TABLE category_product (
product_id integer NOT NULL,
category_id integer NOT NULL
);

ALTER TABLE nette.category_product OWNER TO james;

--
– TOC entry 1953 (class 0 OID 25242)
– Dependencies: 173
– Data for Name: category_product; Type: TABLE DATA; Schema: nette; Owner: james
--

COPY category_product (product_id, category_id) FROM stdin;
1 3
1 2
3 1
13 1
\.

--
– TOC entry 1843 (class 2606 OID 25278)
– Name: category_product_pk; Type: CONSTRAINT; Schema: nette; Owner: james; Tablespace:
--

ALTER TABLE ONLY category_product
ADD CONSTRAINT category_product_pk PRIMARY KEY (category_id, product_id);

--
– TOC entry 1844 (class 2606 OID 25287)
– Name: category_fk; Type: FK CONSTRAINT; Schema: nette; Owner: james
--

ALTER TABLE ONLY category_product
ADD CONSTRAINT category_fk FOREIGN KEY (category_id) REFERENCES category(id) ON UPDATE CASCADE ON DELETE CASCADE;

--
– TOC entry 1845 (class 2606 OID 25297)
– Name: product_fk; Type: FK CONSTRAINT; Schema: nette; Owner: james
--

ALTER TABLE ONLY category_product
ADD CONSTRAINT product_fk FOREIGN KEY (product_id) REFERENCES product(id) ON UPDATE CASCADE ON DELETE CASCADE;

-- Completed on 2015–02–19 06:24:46

--
– PostgreSQL database dump complete
--

Tabulka product

– PostgreSQL database dump
--

-- Dumped from database version 9.3.5
– Dumped by pg_dump version 9.3.5
– Started on 2015–02–19 06:25:05

SET statement_timeout = 0;
SET lock_timeout = 0;
SET client_encoding = ‚UTF8‘;
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;

SET search_path = nette, pg_catalog;

SET default_tablespace = '';

SET default_with_oids = false;

--
– TOC entry 178 (class 1259 OID 25259)
– Name: product; Type: TABLE; Schema: nette; Owner: james; Tablespace:
--

CREATE TABLE product (
id integer NOT NULL,
name character varying(50) NOT NULL,
description character varying(1000),
price numeric,
sold integer DEFAULT 0,
ingredients character varying(1000),
active boolean DEFAULT true NOT NULL,
manager_id integer NOT NULL,
img_ext character varying(10) NOT NULL,
uri character varying(50) NOT NULL
);

ALTER TABLE nette.product OWNER TO james;

--
– TOC entry 179 (class 1259 OID 25267)
– Name: product_id_seq; Type: SEQUENCE; Schema: nette; Owner: james
--

CREATE SEQUENCE product_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER TABLE nette.product_id_seq OWNER TO james;

--
– TOC entry 1962 (class 0 OID 0)
– Dependencies: 179
– Name: product_id_seq; Type: SEQUENCE OWNED BY; Schema: nette; Owner: james
--

ALTER SEQUENCE product_id_seq OWNED BY product.id;

--
– TOC entry 1844 (class 2604 OID 25272)
– Name: id; Type: DEFAULT; Schema: nette; Owner: james
--

ALTER TABLE ONLY product ALTER COLUMN id SET DEFAULT nextval(‚product_id_seq‘::regclass);

--
– TOC entry 1956 (class 0 OID 25259)
– Dependencies: 178
– Data for Name: product; Type: TABLE DATA; Schema: nette; Owner: james
--

COPY product (id, name, description, price, sold, ingredients, active, manager_id, img_ext, uri) FROM stdin;
1 Grog Bylinkový čaj s citronem a je do něj nalit panák rumu 39.5 0 Voda, bylinky, rum, citron, med t 2 jpg grog
3 Turecká káva Káva připravovaná zalitím a spařením mleté zrnkové kávy vroucí vodou. Součástí přípravy je namletí zrnkové kávy. 22.5 0 Voda, káva, cukr t 2 jpg turecka-kava
13 Vídeňská káva Nápoj s dlouholetou historií je charakteristický štědrou porcí šlehačky, která mu dodává nejen neobvyklý, majestátní vzhled s nádechem luxusu, ale díky tomu, že šlehačka nahrazuje zároveň mléko i cukr, tak i chuť. 38.5 0 Voda, káva, šlehaná smetana, práškové kakao t 2 png videnska-kava
\.

--
– TOC entry 1963 (class 0 OID 0)
– Dependencies: 179
– Name: product_id_seq; Type: SEQUENCE SET; Schema: nette; Owner: james
--

SELECT pg_catalog.setval(‚product_id_seq‘, 1, false);

--
– TOC entry 1846 (class 2606 OID 25284)
– Name: product_name_key; Type: CONSTRAINT; Schema: nette; Owner: james; Tablespace:
--

ALTER TABLE ONLY product
ADD CONSTRAINT product_name_key UNIQUE (name);

--
– TOC entry 1848 (class 2606 OID 25286)
– Name: product_pkey; Type: CONSTRAINT; Schema: nette; Owner: james; Tablespace:
--

ALTER TABLE ONLY product
ADD CONSTRAINT product_pkey PRIMARY KEY (id);

-- Completed on 2015–02–19 06:25:05

--
– PostgreSQL database dump complete
--

Editoval James07 (19. 2. 2015 14:08)

David Matějka
Moderator | 6445
+
0
-

proc tam mas

$this->database->table('nette.product')

a ne jen product ?

James07
Člen | 41
+
0
-

@matej21
měl jsem tam tabulku se stejným názvem v jiném schématu, tak nette bylo schéma, ve kterém byla požadovaná tabulka. Teď už to není potřeba, když jsem to dal do jiné databáze. No a po nechání jenom product mi to vyhazuje tu sql chybu, kterou mi to vyhazovalo v prvním příspěvku:

PDOException #42P01

SQLSTATE[42P01]: Undefined table: 7 ERROR: relation „product“ does not exist LINE 21: AND c.oid = ‚„product“‘::regclass ^ search►

SQL

SELECT
a.attname::varchar AS name,
c.relname::varchar AS table,
upper(t.typname) AS nativetype,
NULL AS size,
FALSE AS unsigned,
NOT (a.attnotnull OR t.typtype = ‚d‘ AND t.typnotnull) AS nullable,
pg_catalog.pg_get_expr(ad.adbin, ‚pg_catalog.pg_attrdef‘::regclass)::varchar AS default,
coalesce(co.contype = ‚p‘ AND strpos(ad.adsrc, ‚nextval‘) = 1, FALSE) AS autoincrement,
coalesce(co.contype = ‚p‘, FALSE) AS primary,
substring(pg_catalog.pg_get_expr(ad.adbin, ‚pg_catalog.pg_attrdef‘::regclass)
from ‚nextval[(]"?([^„]+)‘) AS sequence
FROM
pg_catalog.pg_attribute AS a
JOIN pg_catalog.pg_class AS c ON a.attrelid = c.oid
JOIN pg_catalog.pg_type AS t ON a.atttypid = t.oid
LEFT JOIN pg_catalog.pg_attrdef AS ad ON ad.adrelid = c.oid AND ad.adnum = a.attnum
LEFT JOIN pg_catalog.pg_constraint AS co ON co.connamespace = c.relnamespace AND contype = ‚p‘ AND
co.conrelid = c.oid AND a.attnum = ANY(co.conkey)
WHERE
c.relkind IN (‚r‘, ‚v‘)
AND c.oid = ‚"product“‘::regclass
AND a.attnum > 0
AND NOT a.attisdropped
ORDER BY
a.attnum

UPDATE: Problém je pravděpodobně ve schématu. Napsal jsem tento funkční select:

<?php
		return $this->database->query(''
				. ' SELECT product.id, product.name, description, price, sold, ingredients, img_ext, uri'
				. ' FROM nette.product'
				. ' JOIN nette.category_product ON product.id = category_product.product_id'
				. ' JOIN nette.category ON category_product.category_id = category.id'
				. ' WHERE category.name = ?', $cat);
?>

pokud ale smažu název schématu – nette, tak to začne hlásit úplně stejný sql chybový kód. Což mě vede k otázce: „Umí Nette\Database\Table pracovat v PostgreSQL s různými schématy?“

Editoval James07 (20. 2. 2015 23:11)

James07
Člen | 41
+
0
-

Tak jsem nakonec vyřešil ten problém s nefunkčním backjoinem. Nette pravděpodobně neumí pracovat s různými schématy v PostgreSQL, a to ani když jsou všechny tabulky v jednom schématu. Po přesunutí všech tabulek z vlastního schématu s názvem nette do výchozího public mi zdroják funguje.