Doctrine m:n uložení z inverzní strany

Phalanx
Člen | 310
+
0
-

Ahoj,

mám vazbu m:n, v podstatě stejné jako z dokumentace doctrine

<?php

<?php
class Article
{
    private $tags;

    public function addTag(Tag $tag)
    {
        $tag->addArticle($this); // synchronously updating inverse side
        $this->tags[] = $tag;
    }
}

class Tag
{
    private $articles;

    public function addArticle(Article $article)
    {
        $this->articles[] = $article;
    }
}

?>

a nevím, jak mám z inverzní strany, tj. Tag v příkladu, ukládat záznamy.

Když dám

<?php
$tag->addArticle($article);
// persist tag
// flush tag
?>

tak se sice ukládají všechny ostatní hodnoty, ale tagy ne. Musím to navázat z druhé strany nebo se to dělá jinak?

Mockrát díky

Editoval Phalanx (22. 3. 2017 22:57)

David Matějka
Moderator | 6445
+
+1
-

bezne to delam zhruba:

class Article
{
    private $tags;

    public function addTag(Tag $tag)
    {
		if (!$this->tags->contains($tag)) {
			$this->tags->add($tag);
			$tag->addArticle($this);
		}
    }
}

class Tag
{
    private $articles;

    public function addArticle(Article $article)
    {
		if (!$this->articles->contains($article)) {
			$this->articles->add($article);
			$article->addTag($this);
		}
    }
}

takze at to budes volat z jakekoliv strany, tak se obe aktualizuji

Phalanx
Člen | 310
+
0
-

Díky Davide, jenom mám ještě problém s tím, že articles->contains a articles->add hází Call to a member function add() on array

Našel jsem tohle, ale vůbec nevím co byla chyba
https://forum.nette.org/…an-exception

Protože getArticles a getTags mám definovány…

<?php

class Tag {
	/**
	 * Many Articles have Many Tags.
	 * @var Articles[]|ArrayCollection
	 * @ORM\ManyToMany(targetEntity="Articles", mappedBy="tags")
	 */
	private $articles;

	public function __construct()
	{
		$this->articles = new ArrayCollection;
	}

	/**
	 * @return Articles[]
	 */
	public function getArticles()
	{
		$active = Criteria::expr()->eq('published', true);
		$criteria = Criteria::create()->where($active);
		return $this->articles->matching($criteria);
	}
}
?>

edit: a dělá mi to z obou stran

Editoval Phalanx (23. 3. 2017 6:56)

fizzy
Backer | 49
+
+1
-

Najlpsie je vyhnut sa bidrectional many-to-many vazbam – odstranit z tagu vazbu na articles, a namiesto getArticles() pridat do ArticleRepository metodu findActiveByTag

Phalanx
Člen | 310
+
0
-

@fizzy Díky za názor. Já se s Doctrine teprve seznamuju, ale tohle mi nepřijde jako správný krok. Kritérii potřebuju filtrovat hlavně smazané záznamy (soft delete), se kterými už nechci nadále pracovat.

Ale možná k tvému řešení někdy dospěju… :)

Editoval Phalanx (23. 3. 2017 7:31)

fizzy
Backer | 49
+
0
-

soft-delete taktiez sa neodporuca pouzivat :D inak na to je fajn rozsirenie do doctrine, ktore to filtruje automaticky: https://github.com/…neExtensions

Phalanx
Člen | 310
+
0
-

@fizzy Já vím, ale aplikace bez soft delete? To bych se fakt bál dát tu aplikaci komukoliv k používání… O rozšíření taky vím, ale řeším si je sám.

Mohl by mi prosím ještě někdo poradit jak s touhle chybou?
articles->contains a articles->add hází Call to a member function add() on array

fizzy
Backer | 49
+
0
-

nemas asi inicializovanu kolekciu, v konstruktore ju treba zinicializovat:

$this->tags = new ArrayCollection();
Phalanx
Člen | 310
+
0
-

@fizzy Zkontroloval jsem, ale opravdu jsou inicializované z obou stran. Pro jistotu jsem ještě promazal cache, ale pořád tam ta chyba je

Oli
Člen | 1215
+
0
-

Ono soft delete opravdu není nejšťastnější řešení. Pokud předpokládáš, že databáze může být velká (miliony záznamů), tak než soft delete je lepší mít 2 databáze. Jednu „živou“ a jednu „statistickou“. Takže všechno co se smaže přesuneš do „statistické“ databáze. Typickej příklad:
Objednávky. Dokud není objednávka v konečným stavu (doručena, stornována), tak je v „živé“ databázi. Pokud je doručena, tak se smaže z živé databáze a přesune se do „statistické“.

Má minimálně 2 výhody:

  1. Nemusíš řešit soft delete věci, prostě vytáhneš všechno a nikdy nezapomeneš na příznak (isDelete).
  2. Je to o hodně rychlejší (místo milionů záznamů taháš pár tisíc)

Musíš se pak starat o 2 EntityManagery, ale není to problém. Jeden je defaultní a druhej (statistickej) si ručně autowiruješ kam potřebuješ. On stejně bude asi potřeba na celkem málo stránkách…

fizzy
Backer | 49
+
0
-

este som si vsimol, ze nespravne pridavas prvky do kolekcie, articles nie je array ale objekt a treba pridavat nove prvky cez add metodu

this->articles[] = $article; // nespravne
this->articles->add($article); // spravne
Phalanx
Člen | 310
+
0
-

@fizzy Díky za pomoc, už jsem to našel – https://forum.nette.org/…zni-strany-2#…

Měl sem tam ještě další práci s polem… Takže několik hodin zabitých na takové ptákovině.

A ohledně soft delete taky děkuju – přečtu si o tom více.

kleinpetr
Člen | 480
+
0
-

@DavidMatějka Nebude stejny efekt s cascade=persist ?

Editoval kleinpetr (23. 1. 2018 15:37)

kleinpetr
Člen | 480
+
0
-

Resim ted jeden priklad a asi by se mi hodila rada

Priklad:
tabulka product(id, nazev, bla bla)
tabulka kategorie(id, nazev, hodnota1, hodnota2)
tabulka product_category(id_product, id_category)

a ted jde o to, ze mi parsuju nejaka externi data a prijde mi treba
produkt ventilator kategorie ventilatory + nejake dalsi info o kategorii

vysledek by mel byt takovy, zjistit zda dana kategorie uz existuje a pokud ano, tak porovnat a popripade updatnout hodnoty a nasledne ji pripradit k produktu. Pokud neexistuje, tak vytvorit a priradit k produktu.

Muj postup je ted takovy, ze kdyz prochazim data, tak kdyz narazim na kategorii, tak si vytvorim novou entitu a tu si predam do helperu, ktery zjisti zda takovy nazev uz existuje, porovna data a pokud jiz existovala, tak mi vrati tu entitu, a pokud neexistovala, tak persistne tu kterou jsem mu poslal a vrati ji.

Ono to funguje dobre, ale napada me, jestli tuto funkcnost nepridat spis Entite, tj. metoda addCategory($categoryEntity) v ProductEntity dostane nejakou novou entitu a uz se postara o zbytek, to zni asi jako spravny navrh, ale nevim jak v entite persistnu jinou entitu.

tzn. kdyz nastavim cascade=persist a v metode addCategory zjistim, ze takova kategorie neexistuje, tak po zavolani` $this->categories->add($newCategory); ` mi to vytvori automaticky zaznam do tabulky kategorie a zaroven zapise do relacni tabulky. To je OK

Ale kdyz v te metode zjistim, ze uz kategorie existuje, tak ji potrebuji updatnout data a potom persistnout, tudiz asi jedine si do entity natahnout entityManager, ale to se mi nechce, takze tenhle zpusob asi nebude uplne allright.

Editoval kleinpetr (23. 1. 2018 16:04)