Insert přes propojovací tabulku

vanaveno
Člen | 144
+
0
-

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
+
0
-

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
+
0
-

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
+
+1
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-
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)

Mysteria
Člen | 797
+
0
-

Dej to opačně:

$selectedTags = $this->database->table('posts_tags')->where('post_id = ?', $postId)->fetchPairs('tag_id', 'post_id');
vanaveno
Člen | 144
+
0
-

Mysteria napsal(a):

Dej to opačně:

$selectedTags = $this->database->table('posts_tags')->where('post_id = ?', $postId)->fetchPairs('tag_id', 'post_id');

To mi zase hází Value ‚35‘ are out of allowed set [26, 27, 28, 29, 30, 31, 32, 33] in field ‚tag_id‘.

vanaveno
Člen | 144
+
0
-

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
+
+1
-

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
+
0
-

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;
    }

}