Přístup k více modelům v jednom presenteru, přístup k db odkudkoli, problém s backjoin
- James07
- Člen | 41
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
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
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
@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
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
- ta chybova hlaska bude patrne k necemu jinemu, nez ke kodu, co uvadis
- 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
- related
$book = $this->database->table('book')->get($id);
foreach ($book->related('book_tag') as $book_tag) {
echo $book_tag->tag->name;
}
- primo vybrat tagy jednim dotazem
$tags = $this->database->table('tag')->where(':book_tag.book.id', $id);
- David Matějka
- Moderator | 6445
V tom pripade by melo fungovat to, co pises. Ale jak rikam – ta chybova hlaska patrne souvisi s necim jinym…
- James07
- Člen | 41
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
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
Jeste je moznost si v presenterech predavat modely viz https://doc.nette.org/…dependencies#…
- David Matějka
- Moderator | 6445
@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
@Томас
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
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
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
@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
proc tam mas
$this->database->table('nette.product')
a ne jen product
?
- James07
- Člen | 41
@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
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.