Тест на вакансию

Создание REST API на Yii2 (PHP) с Bearer авторизацией

21 августа 2024 г.
181

Установка Yii2

Начнём с установки нового Yii2-проекта с помощью Composer (всё делается в терминале IDE/командной строке):
composer create-project --prefer-dist yiisoft/yii2-app-basic yii2-rest-api
cd yii2-rest-api

Настройка базы данных

В config/db.php:
return [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=localhost;dbname=yii2_rest_api',
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',
];
Создадим базу данных yii2_rest_api.

Миграции

Создадим миграции:
# Создаём миграцию для пользователей
php yii migrate/create create_user_table

# Создаём миграцию для статей
php yii migrate/create create_article_table

# Создаём миграцию для комментариев
php yii migrate/create create_comment_table

Отредактируем миграции

Миграция для пользователей (migrations/mXXXXXX_XXXXXX_create_user_table.php):
public function safeUp()
{
    $tableOptions = null;
    if ($this->db->driverName === 'mysql') {
        $tableOptions = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB';
    }

    $this->createTable('{{%user}}', [
        'id' => $this->primaryKey(),
        'username' => $this->string(255)->notNull()->unique(),
        'email' => $this->string(255)->notNull()->unique(),
        'password_hash' => $this->string(255)->notNull(),
        'auth_key' => $this->string(32)->notNull(),
        'access_token' => $this->string(255)->unique(),
        'created_at' => $this->timestamp()->notNull()->defaultExpression('CURRENT_TIMESTAMP'),
        'updated_at' => $this->timestamp()->notNull()->defaultExpression('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'),
    ], $tableOptions);
}

public function safeDown()
{
    $this->dropTable('{{%user}}');
}
Миграция для статей (migrations/mXXXXXX_XXXXXX_create_article_table.php):
public function safeUp()
{
    $tableOptions = null;
    if ($this->db->driverName === 'mysql') {
        $tableOptions = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB';
    }

    $this->createTable('{{%article}}', [
        'id' => $this->primaryKey(),
        'user_id' => $this->integer()->notNull(),
        'title' => $this->string(255)->notNull(),
        'content' => $this->text()->notNull(),
        'created_at' => $this->timestamp()->notNull()->defaultExpression('CURRENT_TIMESTAMP'),
        'updated_at' => $this->timestamp()->notNull()->defaultExpression('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'),
    ], $tableOptions);

    // Внешний ключ и индексы
    $this->addForeignKey(
        'fk-article-user_id',
        '{{%article}}',
        'user_id',
        '{{%user}}',
        'id',
        'CASCADE',
        'CASCADE'
    );

    $this->createIndex('idx-article-user_id', '{{%article}}', 'user_id');
}

public function safeDown()
{
    $this->dropForeignKey('fk-article-user_id', '{{%article}}');
    $this->dropTable('{{%article}}');
}
Миграция для комментариев (migrations/mXXXXXX_XXXXXX_create_comment_table.php):
public function safeUp()
{
    $tableOptions = null;
    if ($this->db->driverName === 'mysql') {
        $tableOptions = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB';
    }

    $this->createTable('{{%comment}}', [
        'id' => $this->primaryKey(),
        'article_id' => $this->integer()->notNull(),
        'user_id' => $this->integer()->notNull(),
        'content' => $this->text()->notNull(),
        'created_at' => $this->timestamp()->notNull()->defaultExpression('CURRENT_TIMESTAMP'),
        'updated_at' => $this->timestamp()->notNull()->defaultExpression('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'),
    ], $tableOptions);

    // Внешние ключи
    $this->addForeignKey(
        'fk-comment-article_id',
        '{{%comment}}',
        'article_id',
        '{{%article}}',
        'id',
        'CASCADE',
        'CASCADE'
    );

    $this->addForeignKey(
        'fk-comment-user_id',
        '{{%comment}}',
        'user_id',
        '{{%user}}',
        'id',
        'CASCADE',
        'CASCADE'
    );

    // Индексы
    $this->createIndex('idx-comment-article_id', '{{%comment}}', 'article_id');
    $this->createIndex('idx-comment-user_id', '{{%comment}}', 'user_id');
}

public function safeDown()
{
    $this->dropForeignKey('fk-comment-article_id', '{{%comment}}');
    $this->dropForeignKey('fk-comment-user_id', '{{%comment}}');
    $this->dropTable('{{%comment}}');
}
Применение миграций:
php yii migrate/up
Другие полезные команды для миграций:
  • php yii migrate/create - Создаёт новую миграцию.
  • php yii migrate/down - Откатывает приложение, возвращая старые миграции.
  • php yii migrate/fresh - Удаляет все таблицы и их связи. Все миграции сначала.
  • php yii migrate/history - Показывает историю миграций.
  • php yii migrate/mark - Изменяет историю миграций на указанную версию.
  • php yii migrate/new - Показывает новые миграции, которые ещё не применены.
  • php yii migrate/redo - Повторяет последние миграции.
  • php yii migrate/to - Обновляет или откатывает до указанной версии.
  • php yii migrate/up (по умолчанию) - Обновляет приложение, применяя новые миграции.

Создание моделей

Создадим модели:
# Создаём модель для пользователей (если отсутствует)
php yii gii/model --tableName=user --modelClass=User

# Создаём модель для статей
php yii gii/model --tableName=article --modelClass=Article

# Создаём модель для комментариев
php yii gii/model --tableName=comment --modelClass=Comment
Или создадим через веб-интерфейс:
​php yii serve --port=8888
http://localhost:8888/index.php?r=gii
#или
http://localhost:8888/gii

Редактирование сгенерированных моделей

Модель User (models/User.php):
<?php

namespace app\models;

use Yii;
use yii\web\IdentityInterface;

class User extends \yii\db\ActiveRecord implements IdentityInterface
{
    public static function tableName()
    {
        return 'user';
    }
    public function rules()
    {
        return [
            [['username', 'email', 'password_hash', 'auth_key'], 'required'],
            [['username', 'email'], 'unique'],
            [['email'], 'email'],
            [['created_at', 'updated_at'], 'safe'],
            [['username', 'email', 'password_hash', 'access_token'], 'string', 'max' => 255],
            [['auth_key'], 'string', 'max' => 32],
        ];
    }
    public static function findIdentity($id)
    {
        return static::findOne($id);
    }
    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['access_token' => $token]);
    }
    public function getId()
    {
        return $this->id;
    }
    public function getAuthKey()
    {
        return $this->auth_key;
    }
    public function validateAuthKey($authKey)
    {
        return $this->auth_key === $authKey;
    }
    public function validatePassword($password)
    {
        return Yii::$app->security->validatePassword($password, $this->password_hash);
    }
    public function setPassword($password)
    {
        $this->password_hash = Yii::$app->security->generatePasswordHash($password);
    }
    public function generateAccessToken()
    {
        $this->access_token = Yii::$app->security->generateRandomString();
    }
    public function generateAuthKey()
    {
        $this->auth_key = Yii::$app->security->generateRandomString();
    }
    public function beforeSave($insert)
    {
        if (parent::beforeSave($insert)) {
            if ($this->isNewRecord) {
                $this->generateAuthKey();
            }
            return true;
        }
        return false;
    }
    // Relations
    public function getArticles()
    {
        return $this->hasMany(Article::class, ['user_id' => 'id']);
    }
    public function getComments()
    {
        return $this->hasMany(Comment::class, ['user_id' => 'id']);
    }
}
Модель Article (models/Article.php):
<?php

namespace app\models;

use Yii;
use yii\db\ActiveRecord;

class Article extends ActiveRecord
{
    public static function tableName()
    {
        return 'article';
    }
    public function rules()
    {
        return [
            [['user_id', 'title', 'content'], 'required'],
            [['user_id'], 'integer'],
            [['content'], 'string'],
            [['created_at', 'updated_at'], 'safe'],
            [['title'], 'string', 'max' => 255],
            [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::class, 'targetAttribute' => ['user_id' => 'id']],
        ];
    }
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'user_id' => 'User ID',
            'title' => 'Title',
            'content' => 'Content',
            'created_at' => 'Created At',
            'updated_at' => 'Updated At',
        ];
    }
    // Relations
    public function getUser()
    {
        return $this->hasOne(User::class, ['id' => 'user_id']);
    }
    public function getComments()
    {
        return $this->hasMany(Comment::class, ['article_id' => 'id']);
    }
}
Модель Comment (models/Comment.php):
<?php

namespace app\models;

class Comment extends \yii\db\ActiveRecord
{
    public static function tableName()
    {
        return 'comment';
    }
    public function rules()
    {
        return [
            [['article_id', 'user_id', 'content'], 'required'],
            [['article_id', 'user_id'], 'integer'],
            [['content'], 'string'],
            [['created_at', 'updated_at'], 'safe'],
            [['article_id'], 'exist', 'skipOnError' => true, 'targetClass' => Article::class, 'targetAttribute' => ['article_id' => 'id']],
            [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::class, 'targetAttribute' => ['user_id' => 'id']],
        ];
    }
    public function getArticle()
    {
        return $this->hasOne(Article::class, ['id' => 'article_id']);
    }
    public function getUser()
    {
        return $this->hasOne(User::class, ['id' => 'user_id']);
    }
}

Создание контроллеров

Создадим контроллеры:
# Создаём контроллер для пользователей (если отсутствует)
php yii gii/controller --controllerClass=app\controllers\UserController

# Создаём контроллер для статей
php yii gii/controller --controllerClass=app\controllers\ArticleController

# Создаём контроллер для комментариев
php yii gii/controller --controllerClass=app\controllers\CommentController

# Создаём контроллер для авторизации
php yii gii/controller --controllerClass=app\controllers\AuthController
AuthController (controllers/AuthController.php):
<?php

namespace app\controllers;

use Yii;
use yii\rest\Controller;
use app\models\User;

class AuthController extends Controller
{
    public function behaviors()
    {
        $behaviors = parent::behaviors();
       
        // Убираем аутентификацию для регистрации и логина
        unset($behaviors['authenticator']);
       
        // Разрешаем CORS
        $behaviors['corsFilter'] = [
            'class' => \yii\filters\Cors::className(),
            'cors' => [
                'Origin' => ['*'],
                'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
                'Access-Control-Request-Headers' => ['*'],
                'Access-Control-Allow-Credentials' => null,
                'Access-Control-Max-Age' => 86400,
                'Access-Control-Expose-Headers' => [],
            ],
        ];
       
        return $behaviors;
    }
   
    public function actionRegister()
    {
        Yii::info('Register action called', 'auth');
       
        // Получаем raw body и декодим JSON
        $rawBody = Yii::$app->request->getRawBody();
        $data = json_decode($rawBody, true);
       
        Yii::info('Raw data: ' . print_r($data, true), 'auth');
        Yii::info('POST data: ' . print_r(Yii::$app->request->post(), true), 'auth');
       
        if (json_last_error() !== JSON_ERROR_NONE) {
            Yii::$app->response->setStatusCode(400);
            return [
                'success' => false,
                'message' => 'Invalid JSON format',
                'json_error' => json_last_error_msg()
            ];
        }
       
        if (empty($data['username']) || empty($data['email']) || empty($data['password'])) {
            Yii::$app->response->setStatusCode(400);
            return [
                'success' => false,
                'message' => 'Missing required fields',
                'received_data' => $data, // Добавим для отладки
                'required' => ['username', 'email', 'password']
            ];
        }
       
        // Проверяем, существует ли пользователь
        if (User::find()->where(['username' => $data['username']])->exists()) {
            Yii::$app->response->setStatusCode(409);
            return [
                'success' => false,
                'message' => 'Username already exists'
            ];
        }
       
        if (User::find()->where(['email' => $data['email']])->exists()) {
            Yii::$app->response->setStatusCode(409);
            return [
                'success' => false,
                'message' => 'Email already exists'
            ];
        }
       
        $user = new User();
        $user->username = $data['username'];
        $user->email = $data['email'];
        $user->setPassword($data['password']);
        $user->generateAuthKey();
        $user->generateAccessToken();
       
        if ($user->save()) {
            Yii::$app->response->setStatusCode(201);
            return [
                'success' => true,
                'message' => 'User registered successfully',
                'user' => [
                    'id' => $user->id,
                    'username' => $user->username,
                    'email' => $user->email,
                    'access_token' => $user->access_token
                ]
            ];
        } else {
            Yii::$app->response->setStatusCode(422);
            return [
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $user->getErrors()
            ];
        }
    }
   
    public function actionLogin()
    {
        $rawBody = Yii::$app->request->getRawBody();
        $data = json_decode($rawBody, true);
       
        if (empty($data['username']) || empty($data['password'])) {
            Yii::$app->response->setStatusCode(400);
            return [
                'success' => false,
                'message' => 'Username and password are required'
            ];
        }
       
        $user = User::findOne(['username' => $data['username']]);
       
        if ($user && $user->validatePassword($data['password'])) {
            // Генерируем новый токен при каждом входе
            $user->generateAccessToken();
            if ($user->save()) {
                return [
                    'success' => true,
                    'message' => 'Login successful',
                    'data' => [
                        'access_token' => $user->access_token,
                        'user_id' => $user->id,
                        'username' => $user->username
                    ]
                ];
            }
        }
       
        Yii::$app->response->setStatusCode(401);
        return [
            'success' => false,
            'message' => 'Invalid username or password'
        ];
    }
}
ArticleController (controllers/ArticleController.php):
<?php
namespace app\controllers;
use Yii;
use yii\rest\Controller;
use yii\filters\auth\HttpBearerAuth;
use yii\web\ForbiddenHttpException;
use yii\web\NotFoundHttpException;
use app\models\Article;
class ArticleController extends Controller
{
    public function behaviors()
    {
        $behaviors = parent::behaviors();
       
        // Добавляем CORS
        $behaviors['corsFilter'] = [
            'class' => \yii\filters\Cors::className(),
            'cors' => [
                'Origin' => ['*'],
                'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
                'Access-Control-Request-Headers' => ['*'],
                'Access-Control-Allow-Credentials' => null,
                'Access-Control-Max-Age' => 86400,
            ],
        ];
       
        // Добавляем Bearer аутентификацию
        $behaviors['authenticator'] = [
            'class' => HttpBearerAuth::className(),
            'except' => ['index', 'view', 'options'], // Разрешаем просмотр без аутентификации
        ];
       
        return $behaviors;
    }
    /**
     * GET /articles - Получить список всех статей
     */
    public function actionIndex()
    {
        $query = Article::find()->with('user');
       
        // Пагинация
        $page = Yii::$app->request->get('page', 1);
        $perPage = Yii::$app->request->get('per-page', 20);
       
        $dataProvider = new \yii\data\ActiveDataProvider([
            'query' => $query,
            'pagination' => [
                'pageSize' => $perPage,
                'page' => $page - 1, // Yii использует 0-based пагинацию
            ],
            'sort' => [
                'defaultOrder' => [
                    'created_at' => SORT_DESC,
                ]
            ]
        ]);
       
        return $dataProvider;
    }
    /**
     * GET /articles/{id} - Получить конкретную статью
     */
    public function actionView($id)
    {
        $article = Article::find()
            ->where(['id' => $id])
            ->with('user', 'comments')
            ->one();
           
        if (!$article) {
            throw new NotFoundHttpException("Article not found");
        }
       
        return $article;
    }
    /**
     * POST /articles - Создать новую статью
     */
    public function actionCreate()
    {
        $model = new Article();
        $data = Yii::$app->request->post();
       
        $model->load($data, '');
        $model->user_id = Yii::$app->user->id; // Устанавливаем user_id из токена
       
        if ($model->save()) {
            Yii::$app->response->setStatusCode(201);
            return $model;
        } else {
            Yii::$app->response->setStatusCode(422);
            return [
                'errors' => $model->getErrors(),
                'message' => 'Validation failed'
            ];
        }
    }
    /**
     * PUT /articles/{id} - Обновить статью
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);
        $this->checkArticleOwnership($model);
       
        $data = Yii::$app->request->post();
        $model->load($data, '');
       
        if ($model->save()) {
            return $model;
        } else {
            Yii::$app->response->setStatusCode(422);
            return [
                'errors' => $model->getErrors(),
                'message' => 'Validation failed'
            ];
        }
    }
    /**
     * DELETE /articles/{id} - Удалить статью
     */
    public function actionDelete($id)
    {
        $model = $this->findModel($id);
        $this->checkArticleOwnership($model);
       
        if ($model->delete()) {
            Yii::$app->response->setStatusCode(204);
            return null;
        } else {
            Yii::$app->response->setStatusCode(500);
            return [
                'message' => 'Failed to delete article'
            ];
        }
    }
    /**
     * OPTIONS /articles - CORS preflight
     */
    public function actionOptions()
    {
        Yii::$app->response->setStatusCode(200);
        return null;
    }
    /**
     * Найти модель статьи по ID
     */
    protected function findModel($id)
    {
        $model = Article::findOne($id);
        if (!$model) {
            throw new NotFoundHttpException("Article not found");
        }
        return $model;
    }
    /**
     * Проверить, что пользователь является владельцем статьи
     */
    protected function checkArticleOwnership($model)
    {
        if ($model->user_id !== Yii::$app->user->id) {
            throw new ForbiddenHttpException('You can only manage your own articles.');
        }
    }
}
CommentController (controllers/CommentController.php):
<?php

namespace app\controllers;

use Yii;
use yii\rest\Controller;
use yii\filters\auth\HttpBearerAuth;
use yii\web\ForbiddenHttpException;
use yii\web\NotFoundHttpException;
use app\models\Comment;

class CommentController extends Controller
{
    public function behaviors()
    {
        $behaviors = parent::behaviors();
       
        // Добавляем CORS
        $behaviors['corsFilter'] = [
            'class' => \yii\filters\Cors::className(),
            'cors' => [
                'Origin' => ['*'],
                'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
                'Access-Control-Request-Headers' => ['*'],
                'Access-Control-Allow-Credentials' => null,
                'Access-Control-Max-Age' => 86400,
            ],
        ];
       
        // Добавляем Bearer аутентификацию
        $behaviors['authenticator'] = [
            'class' => HttpBearerAuth::className(),
            'except' => ['index', 'view', 'options'], // Разрешаем просмотр без аутентификации
        ];
       
        return $behaviors;
    }
    /**
     * GET /comments - Получить список всех комментариев
     */
    public function actionIndex()
    {
        $query = Comment::find()->with(['user', 'article']);
       
        // Фильтрация по article_id
        $articleId = Yii::$app->request->get('article_id');
        if ($articleId) {
            $query->andWhere(['article_id' => $articleId]);
        }
       
        // Фильтрация по user_id
        $userId = Yii::$app->request->get('user_id');
        if ($userId) {
            $query->andWhere(['user_id' => $userId]);
        }
       
        // Пагинация
        $page = Yii::$app->request->get('page', 1);
        $perPage = Yii::$app->request->get('per-page', 20);
       
        // Сортировка
        $sort = Yii::$app->request->get('sort', '-created_at');
        if ($sort === '-created_at') {
            $query->orderBy(['created_at' => SORT_DESC]);
        } elseif ($sort === 'created_at') {
            $query->orderBy(['created_at' => SORT_ASC]);
        }
       
        $dataProvider = new \yii\data\ActiveDataProvider([
            'query' => $query,
            'pagination' => [
                'pageSize' => $perPage,
                'page' => $page - 1,
            ]
        ]);
       
        return $dataProvider;
    }
    /**
     * GET /comments/{id} - Получить конкретный комментарий
     */
    public function actionView($id)
    {
        $comment = Comment::find()
            ->where(['id' => $id])
            ->with(['user', 'article'])
            ->one();
           
        if (!$comment) {
            throw new NotFoundHttpException("Comment not found");
        }
       
        return $comment;
    }
    /**
     * POST /comments - Создать новый комментарий
     */
    public function actionCreate()
    {
        $model = new Comment();
        $data = Yii::$app->request->post();
       
        $model->load($data, '');
        $model->user_id = Yii::$app->user->id; // Устанавливаем user_id из токена
       
        if ($model->save()) {
            Yii::$app->response->setStatusCode(201);
           
            // Возвращаем комментарий с связанными данными
            return Comment::find()
                ->where(['id' => $model->id])
                ->with(['user', 'article'])
                ->one();
        } else {
            Yii::$app->response->setStatusCode(422);
            return [
                'errors' => $model->getErrors(),
                'message' => 'Validation failed'
            ];
        }
    }
    /**
     * PUT /comments/{id} - Обновить комментарий
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);
        $this->checkCommentOwnership($model);
       
        $data = Yii::$app->request->post();
        $model->load($data, '');
       
        if ($model->save()) {
            // Возвращаем обновлённый комментарий с связанными данными
            return Comment::find()
                ->where(['id' => $model->id])
                ->with(['user', 'article'])
                ->one();
        } else {
            Yii::$app->response->setStatusCode(422);
            return [
                'errors' => $model->getErrors(),
                'message' => 'Validation failed'
            ];
        }
    }
    /**
     * DELETE /comments/{id} - Удалить комментарий
     */
    public function actionDelete($id)
    {
        $model = $this->findModel($id);
        $this->checkCommentOwnership($model);
       
        if ($model->delete()) {
            Yii::$app->response->setStatusCode(204);
            return null;
        } else {
            Yii::$app->response->setStatusCode(500);
            return [
                'message' => 'Failed to delete comment'
            ];
        }
    }
    /**
     * OPTIONS /comments - CORS preflight
     */
    public function actionOptions()
    {
        Yii::$app->response->setStatusCode(200);
        return null;
    }
    /**
     * Найти модель комментария по ID
     */
    protected function findModel($id)
    {
        $model = Comment::findOne($id);
        if (!$model) {
            throw new NotFoundHttpException("Comment not found");
        }
        return $model;
    }
    /**
     * Проверить, что пользователь является владельцем комментария
     */
    protected function checkCommentOwnership($model)
    {
        if ($model->user_id !== Yii::$app->user->id) {
            throw new ForbiddenHttpException('You can only manage your own comments.');
        }
    }
}

Настройка REST API в конфигурации

В config/web.php:
...
    'components' => [
        'request' => [
            'cookieValidationKey' => 'xxxxxxxxxxxxxxxxxxxxx',
            'parsers' => [
                'application/json' => 'yii\web\JsonParser',
            ],
        ],
...
И здесь config/web.php:
...
        'urlManager' => [
            'enablePrettyUrl' => true,
            'enableStrictParsing' => true,
            'showScriptName' => false,
            'rules' => [
                [
                    'class' => 'yii\rest\UrlRule',
                    'controller' => ['user', 'article', 'comment'],
                ],
                'POST auth/login' => 'auth/login',
                'POST auth/register' => 'auth/register',
            ],
        ],
...

Тестирование

Регистрация:
curl -v -X POST http://localhost:8888/auth/register \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","email":"test@example.com","password":"password123"}'
Авторизация:
curl -X POST http://localhost:8888/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","password":"password123"}'
Работа со статьями:
# Получить все статьи
curl -X GET http://localhost:8888/articles

# Получить статью по ID
curl -X GET http://localhost:8888/articles/1

# Создать статью
curl -X POST http://localhost:8888/articles \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"title":"My Article","content":"Article content"}'

# Обновить статью
curl -X PUT http://localhost:8888/articles/1 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"title":"Updated Title","content":"Updated content"}'

# Удалить статью
curl -X DELETE http://localhost:8888/articles/1 \
  -H "Authorization: Bearer YOUR_TOKEN"
Работа с комментариями:
#Получить все комментарии
curl -X GET http://localhost:8888/comments

#Создать комментарий
curl -X POST http://localhost:8888/comments \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "article_id": 1,
    "content": "Отличная статья! Очень полезно."
  }'

#Получить комментарии конкретной статьи
curl -X GET "http://localhost:8888/comments?article_id=1"

#Получить комментарии конкретного пользователя
curl -X GET "http://localhost:8888/comments?user_id=1"

#Получить комментарий по ID
curl -X GET http://localhost:8888/comments/1

#Обновить комментарий
curl -X PUT http://localhost:8888/comments/1 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "content": "Обновлённый текст комментария"
  }'

#Удалить комментарий
curl -X DELETE http://localhost:8888/comments/1 \
  -H "Authorization: Bearer YOUR_TOKEN"

#Комментарии с сортировкой и пагинацией
curl -X GET "http://localhost:8888/comments?article_id=1&sort=-created_at&page=1&per-page=10"

Все команды Gii

  • php yii gii/controller — Генерирует новый контроллер (создаёт файл контроллера с выбранным именем).
  • php yii gii/crud — Генерирует полный CRUD (Create, Read, Update, Delete) для указанной модели и таблицы.
  • php yii gii/extension — Создаёт расширение Yii (расширяет функциональность проекта).
  • php yii gii/form — Генерирует форму для модели (обычно для быстрого создания форм).
  • php yii gii/index — Показывает страницу Gii, где можно выбрать генераторы и настроить их (это основной интерфейс Gii).
  • php yii gii/model — Генерирует модель на основе таблицы базы данных.
  • php yii gii/module — Создаёт новый модуль Yii (отдельный компонент приложения).
Поделиться: