├── log └── .gitignore ├── temp └── .gitignore ├── .github ├── FUNDING.yml └── workflows │ └── build.yml ├── .phpstan.neon ├── .gitignore ├── www ├── css │ └── screen.css ├── index.php └── .htaccess ├── config ├── db.local.example.neon ├── services.neon ├── common.neon └── db.neon ├── migrations ├── structures │ ├── 2014-05-30-002-comments-deleted.sql │ └── 2014-05-30-001-basic-structure.sql └── dummy-data │ └── 2014-05-30-002-basic-testdata.sql ├── app ├── Presenters │ ├── templates │ │ ├── Error │ │ │ ├── 4xx.latte │ │ │ ├── 410.latte │ │ │ ├── 403.latte │ │ │ ├── 404.latte │ │ │ ├── 503.phtml │ │ │ └── 500.phtml │ │ └── @layout.latte │ ├── Error4xxPresenter.php │ └── Error5xxPresenter.php ├── Blog │ ├── Presenters │ │ ├── PostDetailTemplate.php │ │ ├── LayoutTemplate.php │ │ ├── PostListTemplate.php │ │ ├── templates │ │ │ └── Home │ │ │ │ ├── default.latte │ │ │ │ └── detail.latte │ │ └── HomePresenter.php │ └── Model │ │ ├── Tags │ │ ├── TagsMapper.php │ │ ├── TagsRepository.php │ │ └── Tag.php │ │ ├── Posts │ │ ├── PostsMapper.php │ │ ├── PostsRepository.php │ │ └── Post.php │ │ └── Comments │ │ ├── CommentsMapper.php │ │ ├── CommentsRepository.php │ │ └── Comment.php ├── Model │ └── Orm.php ├── Router │ └── RouterFactory.php └── Bootstrap.php ├── bin └── console ├── readme.md ├── composer.json └── license.md /log/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /temp/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: hrach 2 | -------------------------------------------------------------------------------- /.phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 8 3 | treatPhpDocTypesAsCertain: false 4 | paths: 5 | - app 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | log/* 2 | temp/* 3 | vendor/* 4 | !.gitignore 5 | /composer.lock 6 | /config/db.local.neon 7 | /.idea 8 | -------------------------------------------------------------------------------- /www/css/screen.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 3em; 3 | } 4 | 5 | a[href^="#error:"] { 6 | background: red; 7 | color: white; 8 | } 9 | -------------------------------------------------------------------------------- /config/db.local.example.neon: -------------------------------------------------------------------------------- 1 | nextras.dbal: 2 | driver: mysqli 3 | host: 127.0.0.1 4 | database: orm-demo 5 | username: root 6 | password: root 7 | -------------------------------------------------------------------------------- /migrations/structures/2014-05-30-002-comments-deleted.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE `comments` 2 | ADD COLUMN `deleted_at` timestamp NULL ON UPDATE CURRENT_TIMESTAMP AFTER `created_at`; 3 | -------------------------------------------------------------------------------- /config/services.neon: -------------------------------------------------------------------------------- 1 | services: 2 | - OrmDemo\Router\RouterFactory::createRouter 3 | 4 | 5 | search: 6 | - in: %appDir% 7 | classes: 8 | - *Factory 9 | - *Facade 10 | -------------------------------------------------------------------------------- /app/Presenters/templates/Error/4xx.latte: -------------------------------------------------------------------------------- 1 | {block content} 2 |

Oops...

3 | 4 |

Your browser sent a request that this server could not understand or process.

5 | 6 |

error {$httpCode}

7 | -------------------------------------------------------------------------------- /app/Presenters/templates/Error/410.latte: -------------------------------------------------------------------------------- 1 | {block content} 2 |

Page Not Found

3 | 4 |

The page you requested has been taken off the site. We apologize for the inconvenience.

5 | 6 |

error 410

7 | -------------------------------------------------------------------------------- /app/Blog/Presenters/PostDetailTemplate.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class TagsMapper extends DbalMapper 12 | { 13 | } 14 | -------------------------------------------------------------------------------- /app/Blog/Model/Posts/PostsMapper.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class PostsMapper extends DbalMapper 12 | { 13 | } 14 | -------------------------------------------------------------------------------- /app/Blog/Model/Comments/CommentsMapper.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class CommentsMapper extends DbalMapper 12 | { 13 | } 14 | -------------------------------------------------------------------------------- /app/Presenters/templates/Error/403.latte: -------------------------------------------------------------------------------- 1 | {block content} 2 |

Access Denied

3 | 4 |

You do not have permission to view this page. Please try contact the web 5 | site administrator if you believe you should be able to view this page.

6 | 7 |

error 403

8 | -------------------------------------------------------------------------------- /app/Blog/Presenters/LayoutTemplate.php: -------------------------------------------------------------------------------- 1 | */ 12 | public array $flashes; 13 | } 14 | -------------------------------------------------------------------------------- /app/Presenters/templates/Error/404.latte: -------------------------------------------------------------------------------- 1 | {block content} 2 |

Page Not Found

3 | 4 |

The page you requested could not be found. It is possible that the address is 5 | incorrect, or that the page no longer exists. Please use a search engine to find 6 | what you are looking for.

7 | 8 |

error 404

9 | -------------------------------------------------------------------------------- /www/index.php: -------------------------------------------------------------------------------- 1 | createContainer(); 10 | $application = $container->getByType(Application::class); 11 | $application->run(); 12 | -------------------------------------------------------------------------------- /app/Blog/Presenters/PostListTemplate.php: -------------------------------------------------------------------------------- 1 | */ 12 | public ICollection $posts; 13 | } 14 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | createContainer(); 11 | $console = $container->getByType(Application::class); 12 | exit($console->run()); 13 | -------------------------------------------------------------------------------- /app/Blog/Model/Tags/TagsRepository.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class TagsRepository extends Repository 12 | { 13 | static function getEntityClassNames(): array 14 | { 15 | return [Tag::class]; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /config/common.neon: -------------------------------------------------------------------------------- 1 | extensions: 2 | console: Contributte\Console\DI\ConsoleExtension(%consoleMode%) 3 | 4 | 5 | php: 6 | date.timezone: Europe/Prague 7 | 8 | 9 | parameters: 10 | 11 | 12 | application: 13 | errorPresenter: 14 | 4xx: Error4xx 15 | 5xx: Error5xx 16 | mapping: OrmDemo\*\Presenters\*Presenter 17 | 18 | 19 | session: 20 | expiration: 14 days 21 | -------------------------------------------------------------------------------- /app/Blog/Model/Comments/CommentsRepository.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class CommentsRepository extends Repository 12 | { 13 | static function getEntityClassNames(): array 14 | { 15 | return [Comment::class]; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Blog/Presenters/templates/Home/default.latte: -------------------------------------------------------------------------------- 1 | {templateType OrmDemo\Blog\Presenters\PostListTemplate} 2 | {block content} 3 | 4 | {foreach $posts as $post} 5 |

{$post->title}

6 |

Tags: {$tag->name}{sep}, {/sep}

7 |

{$post->content}

8 |
9 | {/foreach} 10 | 11 | {/block} 12 | -------------------------------------------------------------------------------- /config/db.neon: -------------------------------------------------------------------------------- 1 | extensions: 2 | nextras.dbal: Nextras\Dbal\Bridges\NetteDI\DbalExtension 3 | nextras.orm: Nextras\Orm\Bridges\NetteDI\OrmExtension 4 | nextras.migrations: Nextras\Migrations\Bridges\NetteDI\MigrationsExtension 5 | 6 | nextras.orm: 7 | model: OrmDemo\Model\Orm 8 | 9 | nextras.migrations: 10 | dir: %appDir%/../migrations 11 | driver: mysql 12 | dbal: nextras 13 | withDummyData: true 14 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Nextras Orm Demo in Nette Framework 2 | =================================== 3 | 4 | [![PHPStan](https://github.com/nextras/orm-demo-nette/actions/workflows/build.yml/badge.svg)](https://github.com/nextras/orm-demo-nette/actions/workflows/build.yml) 5 | 6 | Demo of [Nextras Orm](https://github.com/nextras/orm). 7 | 8 | Set up: 9 | - configure `config/db.local.neon`, see example file in the same directory; 10 | - run migrations via `bin/console migrations:reset` 11 | -------------------------------------------------------------------------------- /app/Model/Orm.php: -------------------------------------------------------------------------------- 1 | /[/]', 17 | ['module' => 'Blog', 'presenter' => 'Home', 'action' => 'default'], 18 | ); 19 | return $router; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Blog/Model/Tags/Tag.php: -------------------------------------------------------------------------------- 1 | $posts {m:m Post::$tags} 16 | */ 17 | class Tag extends Entity 18 | { 19 | public function __construct(string $name) 20 | { 21 | parent::__construct(); 22 | $this->name = $name; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Blog/Model/Posts/PostsRepository.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class PostsRepository extends Repository 13 | { 14 | /** 15 | * @return ICollection 16 | */ 17 | public function findHomepageOverview(): ICollection 18 | { 19 | return $this->findAll()->orderBy('createdAt', ICollection::DESC); 20 | } 21 | 22 | 23 | static function getEntityClassNames(): array 24 | { 25 | return [Post::class]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Blog/Presenters/templates/Home/detail.latte: -------------------------------------------------------------------------------- 1 | {templateType OrmDemo\Blog\Presenters\PostDetailTemplate} 2 | {block content} 3 | 4 |

{$post->title}

5 |

Tags: {$tag->name}{sep}, {/sep}

6 |

{$post->content}

7 | 8 |
9 | 10 |

Comments:

11 | 12 | {foreach $post->comments as $comment} 13 |
14 | {$comment->name}, {$comment->email} 15 | delete comment 16 |

{$comment->content}

17 |
18 | {/foreach} 19 | 20 | {control addCommentForm} 21 | 22 |
23 | 24 | {control updateTagsForm} 25 | 26 | {/block} 27 | -------------------------------------------------------------------------------- /app/Blog/Model/Comments/Comment.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | Site is temporarily down for maintenance 19 | 20 |

We're Sorry

21 | 22 |

The site is temporarily down for maintenance. Please try again in a few minutes.

23 | -------------------------------------------------------------------------------- /app/Presenters/templates/@layout.latte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {ifset #title}{include title|striptags} | {/ifset}Nette Sandbox 6 | 7 | 8 | {block head}{/block} 9 | 10 | 11 | 12 | 13 | 14 |
{$flash->message}
15 | 16 | {include content} 17 | 18 | 19 | -------------------------------------------------------------------------------- /www/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache configuration file (see httpd.apache.org/docs/current/mod/quickreference.html) 2 | 3 | # disable directory listing 4 | 5 | Options -Indexes 6 | 7 | 8 | # enable cool URL 9 | 10 | RewriteEngine On 11 | # RewriteBase / 12 | 13 | # prevents files starting with dot to be viewed by browser 14 | RewriteRule /\.|^\. - [F] 15 | 16 | # front controller 17 | RewriteCond %{REQUEST_FILENAME} !-f 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteRule !\.(pdf|js|ico|gif|jpg|png|css|rar|zip|tar\.gz)$ index.php [L] 20 | 21 | 22 | # enable gzip compression 23 | 24 | AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/x-javascript text/javascript application/javascript application/json 25 | 26 | -------------------------------------------------------------------------------- /app/Bootstrap.php: -------------------------------------------------------------------------------- 1 | setDebugMode(['127.0.0.1']); 16 | $configurator->enableTracy($appDir . '/log'); 17 | $configurator->setTempDirectory($appDir . '/temp'); 18 | $configurator->createRobotLoader()->addDirectory(__DIR__)->register(); 19 | 20 | $configurator->addConfig($appDir . '/config/common.neon'); 21 | $configurator->addConfig($appDir . '/config/services.neon'); 22 | $configurator->addConfig($appDir . '/config/db.neon'); 23 | $configurator->addConfig($appDir . '/config/db.local.neon'); 24 | 25 | return $configurator; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Presenters/Error4xxPresenter.php: -------------------------------------------------------------------------------- 1 | */ 15 | public array $allowedMethods = []; 16 | 17 | 18 | public function renderDefault(Nette\Application\BadRequestException $exception): void 19 | { 20 | // renders the appropriate error template based on the HTTP status code 21 | $code = $exception->getCode(); 22 | $file = is_file($file = __DIR__ . "/templates/Error/$code.latte") 23 | ? $file 24 | : __DIR__ . '/templates/Error/4xx.latte'; 25 | assert($this->template instanceof Nette\Bridges\ApplicationLatte\Template); 26 | $this->template->httpCode = $code; 27 | $this->template->setFile($file); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "php": ">=8.1", 4 | "nette/application": "^3.2", 5 | "nette/bootstrap": "^3.2", 6 | "nette/caching": "^3.3", 7 | "nette/di": "^3.2", 8 | "nette/finder": "^3.0", 9 | "nette/forms": "^3.2", 10 | "nette/http": "^3.3", 11 | "nette/mail": "^3.1", 12 | "nette/robot-loader": "^4.0", 13 | "nette/security": "^3.2", 14 | "nette/utils": "^4.0", 15 | "latte/latte": "^3.0", 16 | "tracy/tracy": "^2.10", 17 | "nextras/orm": "@dev", 18 | "nextras/dbal": "@dev", 19 | "nextras/migrations": "^3.3", 20 | "contributte/console": "^0.10" 21 | }, 22 | "require-dev": { 23 | "phpstan/phpstan": "^1.10", 24 | "nextras/orm-phpstan": "@dev", 25 | "phpstan/phpstan-nette": "^1.2", 26 | "phpstan/extension-installer": "^1.3" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "OrmDemo\\": "app" 31 | } 32 | }, 33 | "scripts": { 34 | "phpstan": "phpstan analyze -c .phpstan.neon" 35 | }, 36 | "config": { 37 | "allow-plugins": { 38 | "phpstan/extension-installer": true 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Presenters/Error5xxPresenter.php: -------------------------------------------------------------------------------- 1 | getParameter('exception'); 27 | $this->logger->log($exception, ILogger::EXCEPTION); 28 | 29 | // Display a generic error message to the user 30 | return new Responses\CallbackResponse(function (Http\IRequest $httpRequest, Http\IResponse $httpResponse): void { 31 | if (preg_match('#^text/html(?:;|$)#', (string)$httpResponse->getHeader('Content-Type'))) { 32 | require __DIR__ . '/templates/Error/500.phtml'; 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | env: 10 | php-tools: "composer:v2" 11 | 12 | jobs: 13 | phpstan: 14 | name: PHPStan 15 | 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | 22 | - name: Setup PHP with pecl extension 23 | uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: 8.3 26 | 27 | - name: Get composer cache directory 28 | id: composercache 29 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 30 | 31 | - name: Cache dependencies 32 | uses: actions/cache@v2 33 | with: 34 | path: ${{ steps.composercache.outputs.dir }} 35 | key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }} 36 | restore-keys: ${{ runner.os }}-composer- 37 | 38 | - name: Install dependencies 39 | run: composer install --prefer-dist 40 | 41 | - name: Run PHPStan 42 | run: composer phpstan 43 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | =============== 3 | 4 | Copyright (c) 2014 Nextras Project 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /migrations/structures/2014-05-30-001-basic-structure.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `comments` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT, 3 | `post_id` int(11) NOT NULL, 4 | `name` varchar(255) DEFAULT NULL, 5 | `email` varchar(255) DEFAULT NULL, 6 | `content` text NOT NULL, 7 | `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 8 | PRIMARY KEY (`id`), 9 | KEY `post_id` (`post_id`), 10 | CONSTRAINT `comments_ibfk_1` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) 11 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 12 | 13 | 14 | CREATE TABLE `posts` ( 15 | `id` int(11) NOT NULL AUTO_INCREMENT, 16 | `title` varchar(255) NOT NULL, 17 | `content` text NOT NULL, 18 | `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 19 | PRIMARY KEY (`id`) 20 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 21 | 22 | 23 | CREATE TABLE `tags` ( 24 | `id` int(11) NOT NULL AUTO_INCREMENT, 25 | `name` varchar(255) NOT NULL, 26 | PRIMARY KEY (`id`) 27 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 28 | 29 | 30 | CREATE TABLE `posts_x_tags` ( 31 | `post_id` int(11) NOT NULL, 32 | `tag_id` int(11) NOT NULL, 33 | PRIMARY KEY (`post_id`, `tag_id`), 34 | CONSTRAINT `posts_x_tags_ibfk_1` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`), 35 | CONSTRAINT `posts_x_tags_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) 36 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 37 | -------------------------------------------------------------------------------- /app/Presenters/templates/Error/500.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Server Error 5 | 6 | 13 | 14 |
15 |
16 |

Server Error

17 | 18 |

We're sorry! The server encountered an internal error and 19 | was unable to complete your request. Please try again later.

20 | 21 |

error 500

22 |
23 |
24 | 25 | 28 | -------------------------------------------------------------------------------- /app/Blog/Model/Posts/Post.php: -------------------------------------------------------------------------------- 1 | $allComments {1:m Comment::$post} 22 | * @property ManyHasMany $tags {m:m Tag::$posts, isMain=true} 23 | * 24 | * @property-read ICollection $comments {virtual} 25 | */ 26 | class Post extends Entity 27 | { 28 | public function __construct(string $title, string $content) 29 | { 30 | parent::__construct(); 31 | $this->title = $title; 32 | $this->content = $content; 33 | } 34 | 35 | 36 | /** 37 | * @return ICollection 38 | */ 39 | public function getterComments(): ICollection 40 | { 41 | return $this->allComments->toCollection()->findBy(['deletedAt' => null]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/Blog/Presenters/HomePresenter.php: -------------------------------------------------------------------------------- 1 | createTemplate(PostListTemplate::class); 25 | $template->posts = $this->orm->posts->findHomepageOverview(); 26 | $this->sendTemplate($template); 27 | } 28 | 29 | 30 | public function actionDetail(int $id): void 31 | { 32 | $this->post = $this->orm->posts->getByIdChecked($id); 33 | } 34 | 35 | 36 | public function renderDetail(int $id): void 37 | { 38 | if (!$this->post) $this->error(); 39 | 40 | $template = $this->createTemplate(PostDetailTemplate::class); 41 | $template->post = $this->post; 42 | $this->sendTemplate($template); 43 | } 44 | 45 | 46 | protected function createComponentAddCommentForm(): Form 47 | { 48 | $form = new Form; 49 | $form->addText('name', 'Name')->setRequired(); 50 | $form->addText('email', 'E-mail')->setHtmlType('email'); 51 | $form->addTextArea('content', 'Comment'); 52 | $form->addSubmit('submit', 'Add comment'); 53 | $form->onSuccess[] = $this->processAddCommentForm(...); 54 | return $form; 55 | } 56 | 57 | 58 | public function processAddCommentForm(Form $form, ArrayHash $values): void 59 | { 60 | if (!$this->post) $this->error(); 61 | 62 | $comment = new Comment(); 63 | $comment->content = $values->content; 64 | $comment->name = $values->name; 65 | $comment->email = $values->email; 66 | $comment->post = $this->post; 67 | 68 | $this->orm->comments->persistAndFlush($comment); 69 | $this->redirect('this'); 70 | } 71 | 72 | 73 | public function handleDeleteComment(int $commentId): void 74 | { 75 | $comment = $this->orm->comments->getByIdChecked($commentId); 76 | $comment->deletedAt = 'now'; 77 | $this->orm->comments->persistAndFlush($comment); 78 | $this->redirect('this'); 79 | } 80 | 81 | 82 | public function createComponentUpdateTagsForm(): Form 83 | { 84 | if (!$this->post) $this->error(); 85 | 86 | $form = new Form; 87 | $form->addCheckboxList('tags', 'Tags', $this->orm->tags->findAll()->fetchPairs('id', 'name')) 88 | ->setDefaultValue($this->post->tags->getRawValue()); 89 | 90 | $form->addSubmit('submit', 'Save tags'); 91 | $form->onSuccess[] = $this->processUpdateTagsForm(...); 92 | return $form; 93 | } 94 | 95 | 96 | public function processUpdateTagsForm(Form $form, ArrayHash $values): void 97 | { 98 | if (!$this->post) $this->error(); 99 | 100 | $this->post->tags->set($values->tags); 101 | $this->orm->posts->persistAndFlush($this->post); 102 | $this->redirect('this'); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /migrations/dummy-data/2014-05-30-002-basic-testdata.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `comments` (`id`, `post_id`, `name`, `email`, `content`, `created_at`) VALUES 2 | (1, 1, 'Jakub', NULL, 'Wort Dressed Sentinent Being water quite a moment and show thirty speck by the floor. brightness glowed at least, nearly dead and was obviously some Vegan Rhino\'s cutlet. It\'s unpleasantly like hitch hiking slang, as anything. - said Zaphod. - Y', '2009-05-11 07:06:05'), 3 | (2, 2, 'Ondřej', NULL, 'Enormous round and guidance system will jump haven\'t opened through the faintly irritated him - That\'s just to Cassette recorder, every to to thirty seconds of us. Arthur began to have Wow, - said to discover into off with pleased with ', '2009-05-19 23:23:21'), 4 | (3, 2, 'Gabriel', NULL, 'Ape-descendant Arthur Dent, and equally get a stone sundial pedestal housed The mice He looked up sharply. He threw Ford handed the Earth passed an answer. - You know, not even finished Permeated - He adjusted it. Arthur agreed with the time', '2009-05-20 04:40:48'), 5 | (4, 2, 'Jakub', NULL, 'Cracked bell, feet up. - Are you will finally managed to see very strong desire just happens. Yeah, I bother please, the not be, - Missiles? Don\'t talk about the common light Slurrp almost to come and the other bits consequences get there ', '2009-05-20 05:14:31'), 6 | (5, 2, 'Daniel', NULL, 'Emphasized because, as the white mice sniffed irritably decided that the ship that the sweaty dishevelled clothes he was for Arthur shivered with Deep Thought, - protested Ford. - said by your brain was almost, miles is each other. Fook ', '2009-05-20 08:31:40'), 7 | (6, 2, 'Emily', NULL, 'Desk. bubble, the wrong bit and the Earth years, maybe that anyone who could get the Sirius Cybernetics Corporation defines a moment, relax and so I\'ve heard rumors about in all intelligent that one pot shot out before a planet ', '2009-05-20 09:52:00'), 8 | (7, 3, 'Olivia', NULL, 'Silly antennae on the thirty seconds later he said. - Yes, - I\'m President always used to give it then? - Well? - Oh into the cold mud. It was clearly was built, and local affairs that\'s for a wicked grin, laugh did we knew much as the spectacle', '2009-05-28 01:50:18'), 9 | (8, 3, 'Vojtěch', NULL, 'Fact! bubble, the wrong bit and the Earth years, maybe that anyone who could get the Sirius Cybernetics Corporation defines a moment, relax and so I\'ve heard rumors about in all ', '2009-05-28 10:06:31'), 10 | (9, 3, 'William', NULL, 'Protruding from years, maybe even myself? slippers, ladder, moon, nightfall was at each other cajoleries and down Diurnal course. - A man frowned at his semi-cousin that through the faintly irritated him - That\'s just to ', '2009-05-28 17:25:41'), 11 | (10, 3, 'Simon', NULL, 'Minds big hello said Arthur. - I will finally realized that he said, - it was only fooling, - What is an interstellar distances in front partly More gunk music and it had nervously, I ', '2009-05-28 23:25:25'), 12 | (11, 3, 'Amelia', NULL, 'Ape-descendant Arthur Dent, and equally get a stone sundial pedestal housed The mice He looked up sharply. He threw Ford handed the Earth passed an answer. - You know, not even finished Permeated - He adjusted it. Arthur agreed with the time', '2009-05-29 06:19:14'), 13 | (12, 4, 'Emily', NULL, 'Violent noise leapt to thirty seconds later he said. - Yes, - I\'m President always used to give it then? - Well? - Oh into the cold mud. It was clearly was built, and local affairs that\'s for a wicked grin, laugh did we knew ', '2009-06-08 17:07:21'), 14 | (13, 4, 'Jessica', NULL, 'Air cushions ballooned out white mice sniffed irritably decided that the ship that the sweaty dishevelled clothes he was for ', '2009-06-08 21:10:34'), 15 | (14, 4, 'Elias', NULL, 'Demarcation may or the wrong bit and the Earth years, maybe that anyone who could get the Sirius Cybernetics Corporation defines a moment, relax and so I\'ve heard rumors about in all intelligent that one pot shot out before a planet ', '2009-06-09 04:40:35'), 16 | (15, 5, 'Jessica', NULL, 'Hence the slow heavy river Moth; wet of the time fresh whalemeat. At lunchtime? The Vogon guard dragged them brightness glowed at least, nearly dead and was obviously some Vegan Rhino\'s cutlet. It\'s unpleasantly like hitch hiking slang, as Tru', '2009-06-19 01:56:47'), 17 | (16, 5, 'Joshua', NULL, 'Optician almost to come and the other bits consequences get there now. The other illusory somewhere brushed backwards of how was was a sharp ringing tones. - he said Slartibartfast coughed politely. - moment in Stone. It saved a white', '2009-06-19 03:44:05'), 18 | (17, 5, 'Lukas', NULL, 'Desk. bubble, the wrong bit and the Earth years, maybe that anyone who could get the Sirius Cybernetics Corporation defines a moment, relax and so I\'ve heard rumors about in all intelligent that one pot shot out before a planet ', '2009-06-19 07:16:40'), 19 | (18, 5, 'Grace', NULL, 'Dent sat on him. - Yeah, OK, - Oh those doors. There must have something else. Come on, to help him, small really thrash it space that ', '2009-06-19 07:28:33'); 20 | 21 | INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES 22 | (1, 'Thronged with making you doing', 'Out! looked like it. At an anachronism. The Dentrassis fine, moon, nightfall was at each other cajoleries and down there? - said Arthur turned himself up. - The that now six know the Universe, and it to know directly his seemed certain carbon-ba', '2009-05-11 03:31:16'), 23 | (2, 'Jerked himself feet up', 'Refit, and found to come and the other bits consequences get there now. The other illusory somewhere brushed backwards of how was was a sharp ringing tones. - he said Slartibartfast coughed. Otherwise me. - He passed right between was b', '2009-05-19 15:24:30'), 24 | (3, 'Danger', 'Usually had to one would I know, - Oh those doors. There must have something else. Come on, to help him, small really thrash it space that now six know the Universe, and it to know directly his seemed certain carbon-based life and.', '2009-05-28 01:18:15'), 25 | (4, 'Tossed looked like it', 'Busy? - Just shut up, that spaceship and spewed up in emergencies as such, but... - yelled Ford, - said Arthur Dent with me? - said about to a rather into his neck. The President of the planet Bethselamin soft and said, very fast. Very good. For wha', '2009-06-08 13:17:21'), 26 | (5, 'Eddie your eyes...', 'Airlock hatchway into your house down! Ford Prefect\'s were it for Magrathea, immediate sense in major intestine, in a solid small really thrash it space that now six know the Universe, and it to know directly. House Down Once you talked to', '2009-06-18 23:55:45'); 27 | 28 | INSERT INTO `tags` (`id`, `name`) VALUES 29 | (1, 'PHP'), 30 | (2, 'MySQL'), 31 | (3, 'Nette'), 32 | (4, 'Nextras'), 33 | (5, 'PostgreSQL'); 34 | 35 | INSERT INTO `posts_x_tags` (`post_id`, `tag_id`) VALUES 36 | (1, 1), 37 | (1, 2), 38 | (1, 3), 39 | (2, 4), 40 | (3, 4), 41 | (4, 4), 42 | (4, 5), 43 | (5, 1); 44 | --------------------------------------------------------------------------------