Reputation: 3078
I have a simple Yii2 REST application. See:
As you can see, there is only one model Category
, and there is only one controller CategoryController
Category
:
<?php
namespace api\models;
/**
* This is the model class for table "{{%category}}".
*
* @property int $id
* @property string $slug
* @property string $title
* @property int $enabled
*
*/
class Category extends \yii\db\ActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return '{{%category}}';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
['enabled', 'default', 'value' => 0],
[['title'], 'required'],
[['enabled'], 'integer'],
[['slug', 'title'], 'string', 'max' => 255],
[['slug'], 'unique'],
];
}
}
CategoryController
:
<?php
namespace api\controllers;
use yii\rest\ActiveController;
/**
* Class CategoryController
*
* @package api\controllers
*/
class CategoryController extends ActiveController
{
public $modelClass = 'api\models\Category';
}
Then I will pin here my configuration for the application:
config/main.php
<?php
$params = array_merge(
require __DIR__ . '/../../common/config/params.php',
require __DIR__ . '/../../common/config/params-local.php',
require __DIR__ . '/params.php',
require __DIR__ . '/params-local.php'
);
return [
'id' => 'app-api',
'basePath' => dirname(__DIR__),
'language' => 'ru-RU',
'bootstrap' => ['log'],
'controllerNamespace' => 'api\controllers',
'components' => [
'request' => [
'parsers' => [
'application/json' => 'yii\web\JsonParser',
]
],
'response' => [
'class' => 'yii\web\Response',
'format' => 'json'
],
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => true,
'identityCookie' => ['name' => '_identity-frontend', 'httpOnly' => true],
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => 'category',
],
],
],
],
'params' => $params,
];
How I run it? Simple. Just with the help of $ php -S localhost:8900
inside the web
directory.
But when I visit the URL: localhost:8900/categories
I see the following:
{"name":"Not Found","message":"Page not found.","code":0,"status":404,"type":"yii\web\NotFoundHttpException","previous":{"name":"Invalid Route","message":"Unable to resolve the request \"category/index\".","code":0,"type":"yii\base\InvalidRouteException"}}
What does it mean? I suppose that Yii makes the following things.
/categories
request (but Yii can't do that by some unknown for me reason)"name":"Not Found","message":"Page not found.","code":0,"status":404,"type":"yii\\web\\NotFoundHttpException",
But the reason of {"name":"Invalid Route","message":"Unable to resolve the request \"category/index\".","code":0,"type":"yii\\base\\InvalidRouteException"}
is unknown for me.
All of this is strange behavior. I'm just trying to follow along this official guide What is wrong for my configuration?
I have dived deeper(directly inside the entrails of the framework).
I found, that the crash is happening here :
/**
* Creates a controller based on the given controller ID.
*
* The controller ID is relative to this module. The controller class
* should be namespaced under [[controllerNamespace]].
*
* Note that this method does not check [[modules]] or [[controllerMap]].
*
* @param string $id the controller ID.
* @return Controller|null the newly created controller instance, or `null` if the controller ID is invalid.
* @throws InvalidConfigException if the controller class and its file name do not match.
* This exception is only thrown when in debug mode.
*/
public function createControllerByID($id)
{
$pos = strrpos($id, '/');
if ($pos === false) {
$prefix = '';
$className = $id;
} else {
$prefix = substr($id, 0, $pos + 1);
$className = substr($id, $pos + 1);
}
if ($this->isIncorrectClassNameOrPrefix($className, $prefix)) {
return null;
}
$className = preg_replace_callback('%-([a-z0-9_])%i', function ($matches) {
return ucfirst($matches[1]);
}, ucfirst($className)) . 'Controller';
$className = ltrim($this->controllerNamespace . '\\' . str_replace('/', '\\', $prefix) . $className, '\\');
// THE PROBLEM IS HERE !!! WITH THE !class_exists($className)
if (strpos($className, '-') !== false || !class_exists($className)) {
return null;
}
if (is_subclass_of($className, 'yii\base\Controller')) {
$controller = Yii::createObject($className, [$id, $this]);
return get_class($controller) === $className ? $controller : null;
} elseif (YII_DEBUG) {
throw new InvalidConfigException('Controller class must extend from \\yii\\base\\Controller.');
}
return null;
}
the !class_exists($className)
takes this string as argument "api\controllers\CategoryController"
and returns true
. The condition works, and I have null
as result.
What is happening then? The following:
/**
* Runs a controller action specified by a route.
* This method parses the specified route and creates the corresponding child module(s), controller and action
* instances. It then calls [[Controller::runAction()]] to run the action with the given parameters.
* If the route is empty, the method will use [[defaultRoute]].
* @param string $route the route that specifies the action.
* @param array $params the parameters to be passed to the action
* @return mixed the result of the action.
* @throws InvalidRouteException if the requested route cannot be resolved into an action successfully.
*/
public function runAction($route, $params = [])
{
$parts = $this->createController($route);
// $parts IS NULL !!!!!!!!!!
if (is_array($parts)) {
/* @var $controller Controller */
list($controller, $actionID) = $parts;
$oldController = Yii::$app->controller;
Yii::$app->controller = $controller;
$result = $controller->runAction($actionID, $params);
if ($oldController !== null) {
Yii::$app->controller = $oldController;
}
return $result;
}
$id = $this->getUniqueId();
throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".');
}
And I see the familiar exception InvalidRouteException
. Any ideas?
Upvotes: 1
Views: 2215
Reputation: 3078
Ok. The answer is simple. If you are developing new one application with the help of copy and paste from another one (like me), don't forget to update common/config/bootstrap.php
and add new one alias. My new application's name is api
. It means my bootstrap.php
is:
<?php
Yii::setAlias('@common', dirname(__DIR__));
Yii::setAlias('@frontend', dirname(dirname(__DIR__)) . '/frontend');
Yii::setAlias('@backend', dirname(dirname(__DIR__)) . '/backend');
Yii::setAlias('@console', dirname(dirname(__DIR__)) . '/console');
Yii::setAlias('@api', dirname(dirname(__DIR__)) . '/api'); // <- new application!!!
P.S. I use advanced template. Thank you for reading)
Upvotes: 2
Reputation:
You need to specify an index method on the CategoryController
.
IE:
public function actionIndex()
{
return $this->render('index');
}
And offcourse you need to add the template for this method or
Upvotes: 0