Insert přes propojovací tabulku
- vanaveno
- Člen | 144
Dobrý den,
potřeboval bych pomoc. Nevím si rady jak vložit data přes propojovací
tabulku.
Zkouším přidávat tagy přes multiselect k jednotlivým článkům. Jeden článek může mít více tagů, takže jsem si vytvořil propojovací tabulku.
Tabulka posts:
id
title
…
Tabulka tags:
id
name
Tabulka posts_tags:
id
post_id
tag_id
Tabulky jsou vzájemně provázané
post_id míří na id posts
tag_id míří na id tags
<?php
namespace App\Presenters;
use Nette;
use Nette\Application\UI\Form;
use Nette\Utils\ArrayHash;
use Nette\SmartObject;
use Nette\Utils\Image;
class PostPresenter extends Nette\Application\UI\Presenter
{
/** @var Nette\Database\Context */
private $database;
public function __construct(Nette\Database\Context $database)
{
$this->database = $database;
}
public function renderShow($postId)
{
$this->template->post = $this->database->table('posts')->get($postId);
$this->template->postTags = $this->template->post->related('posts_tags');
}
// Vytvoreni postu
protected function createComponentPostForm()
{
$form = new Form;
$form->addText('title', 'Titulek:')
->setRequired();
$form->addText('slug', 'Slug:');
$categories = array();
$select = $this->database->table('categories')->select('id')->select('title')->fetchAll();
foreach ($select as $id => $row) {
$categories[$id] = $row['title'];
}
$form->addSelect('cat_id', 'Kategorie:', $categories)
->setPrompt('Zvolte kategorii')
->setRequired("Zadejte kategorii");
$form->addTextArea('content', 'Obsah:')
->setRequired();
$tags = array();
$select = $this->database->table('tags')->fetchAll();
foreach ($select as $id => $row) {
$tags[$id] = $row['name'];
}
$form->addMultiSelect('cat_id', 'Tagy:', $tags);
/* Zde pořebuji vybrat post_id a vložit do propojovací tabulky posts_tags
post_id => postId,
tag_id => vybarané tagId */
$form->addSubmit('send', 'Publikovat');
$form->onSuccess[] = [$this, 'postFormSucceeded'];
return $form;
}
// Publikovani postu
public function postFormSucceeded(Form $form, $values)
{
$postId = $this->getParameter('postId');
if ($postId) {
$post = $this->database->table('posts')->get($postId);
$post->update($values);
} else {
$post = $this->database->table('posts')->insert($values);
}
$this->flashMessage('Příspěvek byl úspěšně publikován.', 'success');
$this->redirect('Posts:');
}
// Editovani postu
public function actionEdit($postId)
{
$post = $this->database->table('posts')->get($postId);
if (!$post) {
$this->error('Příspěvek nebyl nalezen');
}
$this['postForm']->setDefaults($post->toArray());
}
// Mazani postu
public function actionDelete($postId)
{
if (!$this->getUser()->isLoggedIn()) {
$this->redirect('Sign:in');
}
$post = $this->database->table('posts')->get($postId);
if (!$post) {
$this->error('Příspěvek nebyl nalezen');
}
$this->database->table('posts')->get($postId)->delete();
$this->flashMessage('Prispevek smazan');
$this->redirect('Posts:');
}
}
?>
Prosím, jak vložit data jako jsou post_id a tag_id do propojovací tabulky.
Děkuji za nakopnutí
Vana
Editoval vanaveno (26. 12. 2018 16:54)
- Mysteria
- Člen | 797
Ahoj, s NDBT už jsem dlouho nedělal, takže kód je z hlavy. :)
Teoreticky ti stačí nejdřív smazat všechny tagy pro daný post a pak
projít foreachem hodnoty z toho multiselectu a nainsertovat je.
public function postFormSucceeded(Form $form, $values)
{
$postId = $this->getParameter('postId');
if ($postId) {
$post = $this->database->table('posts')->get($postId);
$post->update($values);
} else {
$post = $this->database->table('posts')->insert($values);
}
$this->database->table('posts_tags')->where('post_id = ?', $post->id)->delete(); // Smažu všechny dosavadní tagy...
foreach ($values->cat_id as $tagId) {
$this->database->table('posts_tags')->insert(['post_id' => $post->id, 'tag_id' => $tagId]); // ... a přidám všechny vybrané
}
$this->flashMessage('Příspěvek byl úspěšně publikován.', 'success');
$this->redirect('Posts:');
}
- vanaveno
- Člen | 144
Mysteria napsal(a):
Ahoj, s NDBT už jsem dlouho nedělal, takže kód je z hlavy. :)
Teoreticky ti stačí nejdřív smazat všechny tagy pro daný post a pak projít foreachem hodnoty z toho multiselectu a nainsertovat je.public function postFormSucceeded(Form $form, $values) { $postId = $this->getParameter('postId'); if ($postId) { $post = $this->database->table('posts')->get($postId); $post->update($values); } else { $post = $this->database->table('posts')->insert($values); } $this->database->table('posts_tags')->where('post_id = ?', $post->id)->delete(); // Smažu všechny dosavadní tagy... foreach ($values->cat_id as $tagId) { $this->database->table('posts_tags')->insert(['post_id' => $post->id, 'tag_id' => $tagId]); // ... a přidám všechny vybrané } $this->flashMessage('Příspěvek byl úspěšně publikován.', 'success'); $this->redirect('Posts:'); }
Díky za pomoc, ale nepomohlo mi to. Ta funkce postFormSucceeded(Form $form, $values). Primárně aktualizuje tabulku posts hodnotami $values. Já ještě právě nevím jak nakrmit dvě tabulky. Jednu posts a druhou posts-tags hodnotami post_id a tag_id. To co si mi poslal, krom toho delete jsem zkoušel, vyhazuje mi to tuhle chybu.
http://netteblog-us.svethostingu-tmp.cz/chyby/42S22.png
Editoval vanaveno (27. 12. 2018 13:32)
- Mysteria
- Člen | 797
Dvě tabulky najednou neuděláš, musíš to udělat po jedné, tzn. nejdřív tu tabulku postů. To ti teďka padá na tom, že ty máš v datech tag_id (což je ale z další tabulky) a ty musíš do toho insertu nebo updatu posílat jenom ty hodnoty, které jsou přímo v té tabulce postů. Takže aby jsi mohl vůbec něco udělat s tou tabulkou postů, tak nejdřív musíš z dat odstranit ten tag, takže třeba takhle:
$postId = $this->getParameter('postId');
$valuesWithoutTag = $values;
unset($valuesWithoutTag['tag_id']);
if ($postId) {
$post = $this->database->table('posts')->get($postId);
$post->update($valuesWithoutTag);
} else {
$post = $this->database->table('posts')->insert($valuesWithoutTag);
}
Pak by ti měl projít insert / update postu a můžeš začít řešit případný problémy s ukládáním do té vazební tabulky.
- vanaveno
- Člen | 144
Mysteria napsal(a):
>
Dvě tabulky najednou neuděláš, musíš to udělat po jedné, tzn. nejdřív tu tabulku postů. To ti teďka padá na tom, že ty máš v datech tag_id (což je ale z další tabulky) a ty musíš do toho insertu nebo updatu posílat jenom ty hodnoty, které jsou přímo v té tabulce postů. Takže aby jsi mohl vůbec něco udělat s tou tabulkou postů, tak nejdřív musíš z dat odstranit ten tag, takže třeba takhle:
$postId = $this->getParameter('postId'); $valuesWithoutTag = $values; unset($valuesWithoutTag['tag_id']); if ($postId) { $post = $this->database->table('posts')->get($postId); $post->update($valuesWithoutTag); } else { $post = $this->database->table('posts')->insert($valuesWithoutTag); }
Pak by ti měl projít insert / update postu a můžeš začít řešit případný problémy s ukládáním do té vazební tabulky.
Misteria díky moc,
funguje to :)
$valuesWithoutTag = $values;
unset($valuesWithoutTag['tag_id']);
if ($postId) {
$post = $this->database->table('posts')->get($postId);
$post->update(['id' => $postId, 'title' => $post->title, 'slug' => $post->slug, 'content' => $post->content, 'image' => $imagename ]);
} else {
$post = $this->database->table('posts')->insert(['title' => $valuesWithoutTag->title, 'cat_id' => $valuesWithoutTag->cat_id, 'slug' => $valuesWithoutTag->slug, 'content' => $valuesWithoutTag->content, 'image' => $imagename ]);
}
$postId = $this->getParameter('postId');
$values = $form->getValues();
$this->database->table('posts_tags')->where('post_id = ?', $post->id)->delete(); // Smažu všechny dosavadní tagy...
foreach ($values->tag_id as $tagId) {
$this->database->table('posts_tags')->insert(['post_id' => $post->id, 'tag_id' => $tagId]); // ... a přidám všechny vybrané
}
$this->flashMessage('Příspěvek byl úspěšně publikován.', 'success');
$this->redirect('Posts:');
- vanaveno
- Člen | 144
No ještě to není úplně dokonalé. Tagy lze kk článku vložit přes propojovací tabulku, ovšem problém je, když chci ty tagy editovat. V tom multiselectu se mi nezobrazí tagy které jsem vybral, to bude asi kódem:
$tags = array();
$select = $this->database->table('tags')->fetchAll();
foreach ($select as $id => $row) {
$tags[$id] = $row['name'];
}
$form->addMultiSelect('tag_id', 'Tagy:', $tags);
Vybírá si to přímo z tabulky tags, ale asi by to chtělo, aby to vybíralo přes tu spojovací tabulku. Nebo jak docílit toho, že po editování článku mi zůstanou vybrané tagy, které jsem vybral v předešlém kroku?
Druhý problém je viz kód níže. Tento kód vymaže předešlé tagy k článku, což je sice dobré, aby se tagy zbytečně nekupily, ale zase po aktualizaci článku, ty tagy zmizí, pokud je ovšem opět neoznačím.
Pomalu do toho z vaší pomocí proníkám, ale je to ještě boj.
Díky za radu
> Mysteria napsal(a):
> >
> > Dvě tabulky najednou neuděláš, musíš to udělat po jedné, tzn. nejdřív tu tabulku postů. To ti teďka padá na tom, že ty máš v datech tag_id (což je ale z další tabulky) a ty musíš do toho insertu nebo updatu posílat jenom ty hodnoty, které jsou přímo v té tabulce postů. Takže aby jsi mohl vůbec něco udělat s tou tabulkou postů, tak nejdřív musíš z dat odstranit ten tag, takže třeba takhle:
> >
> > /--php
> > $postId = $this->getParameter('postId');
> > $valuesWithoutTag = $values;
> > unset($valuesWithoutTag['tag_id']);
> >
> > if ($postId) {
> > $post = $this->database->table('posts')->get($postId);
> > $post->update($valuesWithoutTag);
> > } else {
> > $post = $this->database->table('posts')->insert($valuesWithoutTag);
> > }
> > \--
> >
> > Pak by ti měl projít insert / update postu a můžeš začít řešit případný problémy s ukládáním do té vazební tabulky.
>
> Misteria díky moc,
> funguje to :)
>
> /--php
> $valuesWithoutTag = $values;
> unset($valuesWithoutTag['tag_id']);
>
> if ($postId) {
> $post = $this->database->table('posts')->get($postId);
> $post->update(['id' => $postId, 'title' => $post->title, 'slug' => $post->slug, 'content' => $post->content, 'image' => $imagename ]);
> } else {
> $post = $this->database->table('posts')->insert(['title' => $valuesWithoutTag->title, 'cat_id' => $valuesWithoutTag->cat_id, 'slug' => $valuesWithoutTag->slug, 'content' => $valuesWithoutTag->content, 'image' => $imagename ]);
> }
>
>
> $postId = $this->getParameter('postId');
> $values = $form->getValues();
> $this->database->table('posts_tags')->where('post_id = ?', $post->id)->delete(); // Smažu všechny dosavadní tagy...
> foreach ($values->tag_id as $tagId) {
> $this->database->table('posts_tags')->insert(['post_id' => $post->id, 'tag_id' => $tagId]); // ... a přidám všechny vybrané
> }
>
>
> $this->flashMessage('Příspěvek byl úspěšně publikován.', 'success');
> $this->redirect('Posts:');
>
> \--
Editoval vanaveno (29. 12. 2018 12:18)
- Mysteria
- Člen | 797
Nezobrazí se ti, protože je tam nikde nenastavuješ jako defaultní hodnoty. :) Mimochodem tvůj kód na výpis všech tagů do selectboxu lze jednoduše zkrátit. Takže třeba takhle:
$tags = $this->database->table('tags')->fetchPairs('id', 'name');
$selectedTags = $this->database->table('posts_tags')->where('post_id = ?', $postId)->fetchPairs('id', 'id');
$form->addMultiSelect('tag_id', 'Tagy:', $tags)->setDefaultValue($selectedTags);
Tím se ti vyřeší problém číslo dvě, protože budou předvybraný tagy, tak ti nebudou mizet, když je nezaklikáš znova, protože už budou předvybraný.
- vanaveno
- Člen | 144
Ahoj, hlásí mi to Undefined variable: postId. Nerozumím tomu ->fetchPairs(‚id‘, ‚id‘). tabulka má sloupce post_id a tag_id to si vybere id posts a id tags?
Mysteria napsal(a):
Nezobrazí se ti, protože je tam nikde nenastavuješ jako defaultní hodnoty. :) Mimochodem tvůj kód na výpis všech tagů do selectboxu lze jednoduše zkrátit. Takže třeba takhle:
$tags = $this->database->table('tags')->fetchPairs('id', 'name'); $selectedTags = $this->database->table('posts_tags')->where('post_id = ?', $postId)->fetchPairs('id', 'id'); $form->addMultiSelect('tag_id', 'Tagy:', $tags)->setDefaultValue($selectedTags);
Tím se ti vyřeší problém číslo dvě, protože budou předvybraný tagy, tak ti nebudou mizet, když je nezaklikáš znova, protože už budou předvybraný.
Editoval vanaveno (30. 12. 2018 18:13)
- vanaveno
- Člen | 144
Já jsem to upravil na tohle, to mi funguje dokonce mi to ukazuje i vybrané tagy, ale bohužel jenom jeden, i když jich mám vybraných více.
$postId = $this->getParameter('postId');
$tags = $this->database->table('tags')->fetchPairs('id', 'name');
$selectedTags = $this->database->table('posts_tags')->where('post_id = ?', $postId)->fetchPairs('post_id', 'tag_id');
$form->addMultiSelect('tag_id', 'Tagy:', $tags)->setDefaultValue($selectedTags);
Editoval vanaveno (30. 12. 2018 18:22)
- vanaveno
- Člen | 144
Když mám vybraný víc jak jeden tag, tak se mi označí jenom jeden ten
tag. A zjistil jsem, že když chci napsat nový příspěvek, taky mi to
vyhodí chybu Column operator does not accept null argument.
a je červeně označena
$selectedTags = $this->database->table(‚posts_tags‘)->where(‚post_id = ?‘, $postId)->fetchPairs(‚post_id‘, ‚tag_id‘);
Editoval vanaveno (30. 12. 2018 18:36)
- Mysteria
- Člen | 797
Už to vidím, ->fetchPairs('tag_id', 'post_id');
jsem
poradil špatně, musí tam být
->fetchPairs('tag_id', 'tag_id');
. Tzn takto:
$selectedTags = $this->database->table('posts_tags')->where('post_id = ?', $postId)->fetchPairs('tag_id', 'tag_id');
Každopádně nakonec jsem těch deset minut tomu obětoval a napsal si to komplet, tak pokud ti to stále nebude fungovat, tak se můžeš inspirovat:
<?php declare(strict_types=1);
namespace App\Presenters;
use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;
use Nette\Database\Context;
/**
* Class HomepagePresenter
*
* @package App\Presenters
*/
final class HomepagePresenter extends Presenter
{
/**
* @var Context
*/
private $database;
public function __construct(Context $database)
{
parent::__construct();
$this->database = $database;
}
/**
*
*/
public function actionList(): void
{
$this->template->posts = $this->database->table('posts');
}
/**
* @param int $id
*/
public function actionChange(int $id): void
{
}
/**
* @param int $id
*/
public function handleDelete(int $id): void
{
$this->database->table('posts_tags')->where('post_id = ?', $id)->delete();
$this->database->table('posts')->where('id = ?', $id)->delete();
$this->redirect('list');
}
/**
* @return Form
*/
protected function createComponentChangeForm(): Form
{
$id = (int) $this->getParameter('id');
$form = new Form();
$form->addText('title', 'Title');
$form->addMultiSelect('tags', 'Tags', $this->database->table('tags')->fetchPairs('id', 'name'));
$form->addSubmit('submit', $id === 0 ? 'Create post' : 'Update post');
if ($id !== 0) {
$post = $this->database->table('posts')->get($id);
$form->setDefaults(
array_merge($post->toArray(), ['tags' => $post->related('posts_tags')->fetchPairs('tag_id', 'tag_id')])
);
}
$form->onSuccess[] = function (Form $form) use ($id): void {
$data = $form->getValues(TRUE);
$tags = $data['tags'];
unset($data['tags']);
if ($id === 0) {
$post = $this->database->table('posts')->insert($data);
foreach ($tags as $tag) {
$this->database->table('posts_tags')->insert(['post_id' => $post->id, 'tag_id' => $tag]);
}
} else {
$this->database->table('posts')->where('id = ?', $id)->update($data);
$this->database->table('posts_tags')->where('post_id = ?', $id)->delete();
foreach ($tags as $tag) {
$this->database->table('posts_tags')->insert(['post_id' => $id, 'tag_id' => $tag]);
}
}
$this->redirect('list');
};
return $form;
}
}
- vanaveno
- Člen | 144
Mysteria díky moc za pomoc :) Udělal jsem to sice jinak, ale tvůj návod mi hodně pomohl, akorát nechápu proč to musí být nastavené fetchPairs(‚tag_id‘, ‚tag_id‘);
Jinak jde vidět, že to lze napsat několika způsoby. Já jsem úplný začátečník, tak to zatím mastím, jak umím, ale začalo mě to bavit, tak snad do toho časem proniknu.
Mysteria napsal(a):
Už to vidím,
->fetchPairs('tag_id', 'post_id');
jsem poradil špatně, musí tam být->fetchPairs('tag_id', 'tag_id');
. Tzn takto:$selectedTags = $this->database->table('posts_tags')->where('post_id = ?', $postId)->fetchPairs('tag_id', 'tag_id');
Každopádně nakonec jsem těch deset minut tomu obětoval a napsal si to komplet, tak pokud ti to stále nebude fungovat, tak se můžeš inspirovat:
<?php declare(strict_types=1); namespace App\Presenters; use Nette\Application\UI\Form; use Nette\Application\UI\Presenter; use Nette\Database\Context; /** * Class HomepagePresenter * * @package App\Presenters */ final class HomepagePresenter extends Presenter { /** * @var Context */ private $database; public function __construct(Context $database) { parent::__construct(); $this->database = $database; } /** * */ public function actionList(): void { $this->template->posts = $this->database->table('posts'); } /** * @param int $id */ public function actionChange(int $id): void { } /** * @param int $id */ public function handleDelete(int $id): void { $this->database->table('posts_tags')->where('post_id = ?', $id)->delete(); $this->database->table('posts')->where('id = ?', $id)->delete(); $this->redirect('list'); } /** * @return Form */ protected function createComponentChangeForm(): Form { $id = (int) $this->getParameter('id'); $form = new Form(); $form->addText('title', 'Title'); $form->addMultiSelect('tags', 'Tags', $this->database->table('tags')->fetchPairs('id', 'name')); $form->addSubmit('submit', $id === 0 ? 'Create post' : 'Update post'); if ($id !== 0) { $post = $this->database->table('posts')->get($id); $form->setDefaults( array_merge($post->toArray(), ['tags' => $post->related('posts_tags')->fetchPairs('tag_id', 'tag_id')]) ); } $form->onSuccess[] = function (Form $form) use ($id): void { $data = $form->getValues(TRUE); $tags = $data['tags']; unset($data['tags']); if ($id === 0) { $post = $this->database->table('posts')->insert($data); foreach ($tags as $tag) { $this->database->table('posts_tags')->insert(['post_id' => $post->id, 'tag_id' => $tag]); } } else { $this->database->table('posts')->where('id = ?', $id)->update($data); $this->database->table('posts_tags')->where('post_id = ?', $id)->delete(); foreach ($tags as $tag) { $this->database->table('posts_tags')->insert(['post_id' => $id, 'tag_id' => $tag]); } } $this->redirect('list'); }; return $form; } }