# Step by step: your module from scratch ← [Back to the “Modules” section](README.md) Let's put together a minimal but working module **"Bookmarks"** (`bookmarks`) - a list of personal admin links. Let's go all the way: folder → descriptor → migrations → routes → controller/model → template → assets → check. > Analysis of components - in [module.php](module-php.md), [files.md](files.md), > [migrations.md](migrations.md), [contributions.md](contributions.md). --- ## 1. Folder ``` adminx/modules/Bookmarks/ ├── module.php ├── Controller.php ├── Model.php ├── migrations/ │ ├── 001_create_admin_bookmarks.sql │ └── uninstall.sql ├── view/ │ └── index.twig └── assets/ ├── bookmarks.less └── bookmarks.css (собирается из .less) ``` --- ## 2. Rights and menus We will declare static rights and menu items directly in `module.php` in step 7. For them no need for separate classes. Provider is written to the file only when it is calculated or reused logic. --- ## 3. Migrations `migrations/001_create_admin_bookmarks.sql`: ```sql CREATE TABLE IF NOT EXISTS `{{prefix}}_admin_bookmarks` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` INT UNSIGNED NOT NULL, `title` VARCHAR(200) NOT NULL DEFAULT '', `url` VARCHAR(500) NOT NULL DEFAULT '', `created_at` INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` `migrations/uninstall.sql`: ```sql DROP TABLE IF EXISTS `{{prefix}}_admin_bookmarks`; ``` There is no need to register rights separately via SQL: when installing the manager synchronizes the `permissions.items` array with the system registry. --- ## 4. Model – `Model.php` ```php getAll() ?: array(); } public static function create($userId, $title, $url) { DB::Insert(self::table(), array( 'user_id' => (int) $userId, 'title' => trim((string) $title), 'url' => trim((string) $url), 'created_at' => time(), )); return (int) DB::insertId(); } public static function delete($id, $userId) { DB::Delete(self::table(), 'id = %i AND user_id = %i', (int) $id, (int) $userId); return DB::affectedRows() > 0; } } ``` ## 5. Controller - `Controller.php` ```php render('@bookmarks/index.twig', array( 'bookmarks' => Model::all(Auth::id()), 'can_manage' => Permission::check('manage_bookmarks'), )); } public function store(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } $errors = Valid::check($_POST, array('title' => 'required', 'url' => 'required|url')); if ($errors) { return $this->error('Проверьте поля', $errors); } Model::create(Auth::id(), Request::postStr('title'), Request::postStr('url')); return $this->success('Закладка добавлена'); } public function delete(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } $id = isset($params['id']) ? (int) $params['id'] : 0; if (!Model::delete($id, Auth::id())) { return $this->error('Не найдено', array(), 404); } return $this->success('Удалено'); } protected function guard() { if (($err = $this->csrfGuard()) !== null) { return $err; } return Permission::check('manage_bookmarks') ? null : $this->error('Недостаточно прав', array(), 403); } } ``` --- ## 6. Template - `view/index.twig` ```twig {% extends '@adminx/main.twig' %} {% block title %}Закладки{% endblock %} {% block content %}
Личные ссылки