CakePHP3 CRUD API and API Routing - crud

I am using the Friends of Cake CRUD plugin for my back-end API. I am also using API prefixes for my routes:
Router::prefix('Api', function ($routes) {
$routes->extensions(['json', 'xml', 'ajax']);
$routes->resources('Messages');
$routes->resources('ReportedListings');
$routes->fallbacks('InflectedRoute');
});
So far so good. My controller is as follows:
namespace App\Controller\Api;
use App\Controller\AppController;
use Cake\Event\Event;
use Cake\Core\Exception\Exception;
class MessagesController extends AppController {
use \Crud\Controller\ControllerTrait;
public function initialize() {
parent::initialize();
$this->loadComponent(
'Crud.Crud', [
'actions' => [
'Crud.Add',
'update' => ['className' => 'Crud.Edit']
],
'listeners' => ['Crud.Api'],
]
,'RequestHandler'
);
$this->Crud->config(['listeners.api.exceptionRenderer' => 'App\Error\ExceptionRenderer']);
$this->Crud->addListener('relatedModels', 'Crud.RelatedModels');
}
public function beforeFilter(Event $event){
parent::beforeFilter($event);
}
public function add() {
return $this->Crud->execute();
}
When I make a call as follows:
[POST] /api/messages.json
I get an error:
Action MessagesController::index() could not be found, or is not accessible.
I instead use:
[POST] /messages.json
I don't get the error and I can add a message. So the question is why with my api prefix routing does the CRUD look for index and how can I avoid this behavior?

I found the issue:
Router::prefix('Api', function ($routes) {
....
}
The 'Api' should be lowercase!
Router::prefix('api', function ($routes) {
....
}

Related

how can i Cache object and its relations in laravel 6

In RatingController:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Resources\RatingResource;
use App\Models\Rating\Rating;
class RatingController extends Controller
{
public function getRatingsByCategory($id)
{
$ratings = cache()->rememberForever('test', function () use ($id) {
return new RatingResource(Rating::findOrFail($id));
});
return response()->json([
'data' => $ratings
]);
}
}
in RatingResource class
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceCollection;
class RatingResource extends JsonResource
{
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->rating_id,
'is_active' => $this->is_active,
'created_at' => '2020',
];
}
}
when i send request to this controller, i get this result
{
"data": {
"id": 3,
"is_active": 1,
"created_at": "2020"
}
}
but when i change each of column in RatingResource like created_at, the result changes.
indeed laravel does not cache final result, it cache the object of my request and when i return that object,it start to convert my object to resource format.
how can i cache the result of ApiResource till i delete this cache key.
You could cache the whole response, this way you would have to add the id to the cache key though, in order to differentiate between the different rating ids.
Something like this:
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Resources\RatingResource;
use App\Models\Rating\Rating;
use Illuminate\Support\Facades\Cache;
class RatingController extends Controller
{
public function getRatingsByCategory($id)
{
return Cache::rememberForever("test:{$id}", function () use ($id) {
return RatingResource(Rating::findOrFail($id));
});
}
}

How to override error handler from a module

To override error handler from a module based this samdark guid:
class Module extends \yii\base\Module
{
public function init()
{
parent::init();
\Yii::configure($this, [
'components' => [
'errorHandler' => [
'class' => ErrorHandler::className(),
]
],
]);
/** #var ErrorHandler $handler */
$handler = $this->get('errorHandler');
\Yii::$app->set('errorHandler', $handler);
$handler->register();
}
}
This method only worked for valid module controllers, but can not handle invalid module controllers. Even affects all over the application so that module error handler will be default error handler all over the application. i was confused how can solve it!
Example: I was willing www.site.com/module/x handled with module error handler(www.site.com/module/default/error),where x is not a valid module controller. If that was not the case, it was handled with app error handler(www.site.com/site/error).
Finally i used this code: (admin is my module name). Know everything is fine.
class AdminModule extends \yii\base\Module
{
public function init()
{
parent::init();
// Is better used regex method, temporally i used this method
$r = Yii::$app->urlManager->parseRequest(Yii::$app->request)[0];
$r_array = explode('/',$r);
if($r_array[0] === 'admin'){
Yii::configure($this, [
'components' => [
'errorHandler' => [
'class' => ErrorHandler::className(),
'errorAction' => '/admin/default/error',
]
],
]);
$handler = $this->get('errorHandler');
Yii::$app->set('errorHandler', $handler);
$handler->register();
}
}
}
If you know a better way please share with me. Thanks

Laravel Route::controller routing issue with prefix

I have following implicit route defined (Laravel 5.2)
// Handle locale
Route::group([
'prefix' => '{country}/{language}',
], function () {
Route::controller('user', 'UserController');
});
And here is my controller
class UserController extends BaseLocaleController
{
public function getIndex()
{
return view('user/index');
}
public function getProfile($slug)
{
echo $slug;die;
return view('user/view');
}
}
My URI Structure is
http://{host}/in/en/user/profile/manju
The problem here, my slug value is in instead of manju. Is there any URI pattern I need to apply?
How can make this work in Laravel 5.2. As you could see, I have country and language prefix in Route::group.
Just pass the $country, $language to the method
So it should be
public function getProfile($country, $language, $slug)
{
echo $slug;die;
return view('user/view');
}

How to call a function within the same controller?

I have to call a soap service using laravel and done so correctly. This soap service requires me to send a login request prior to sending any other request.
The code I'm using works, but I want to improve by removing the login from all the functions and creating one function.
I tried changing the following for one function:
public function getcard($cardid)
{
SoapWrapper::add(function ($service) {
$service
->name('IS')
->wsdl(app_path().'\giftcard.wsdl')
->trace(true);
});
$data = [
'UserName' => 'xxxx',
'Password' => 'xxxx',
];
$card = [
'CardId' => $cardid,
];
SoapWrapper::service('IS', function ($service) use ($data,$card) {
$service->call('Login', [$data]);
$cardinfo=$service->call('GetCard', [$card]);
dd($cardinfo->Card);
});
}
Into:
public function login()
{
SoapWrapper::add(function ($service) {
$service
->name('IS')
->wsdl(app_path().'\giftcard.wsdl')
->trace(true);
});
$data = [
'UserName' => 'xxxx',
'Password' => 'xxxx',
];
SoapWrapper::service('IS', function ($service) use ($data) {
return $service->call('Login', [$data]);
//$service->call('Login', [$data]);
//return $service;
});
}
public function getcard($cardid)
{
$this->login();
$card = [
'CardId' => $cardid,
];
$cardinfo=$service->call('GetCard', [$card]);
dd($card);
}
But this doesn't work. I also tried it with the commented out part, but that doesn't work. Both options result in an error that it didn't find 'service'.
I know it has something to do with oop, but don't know any other option.
I took this as an example, but I probably implemented it wrong?
So my question is: How do I reuse the login part for all other functions?
Your return statement in the login() method is within the scope of that closure. You need to return the result of the closure as well.
return SoapWrapper::service('IS', function ($service) use ($data) {
return $service->call('Login', [$data]);
});
EDIT:
To explain a little bit. You have a function:
SoapWrapper::service('IS' ,function() {}
Inside of a function : public function login()
If you need to return data from your login() method, and that data is contained within your SoapWrapper::service() method, then both methods need a return statement

yii friendly url for modules does not work

I created a modules called calculator and am trying to create the freindly url for it.
here is my code
'<action:(calc1|calc2|calc3)>' => '/calculator/<action>',
the view files are in
modules/calculator/view/default/pages/calc2.php
when i type in my address bar
localhost/dev/cal2
i get this error
Error 404
Unable to resolve the request "calculator/calc2".
in my controller i have this
class DefaultController extends Controller
{
public function actions() {
return array(
'page' => array('class' => 'CViewAction'),
);
}
public function actionIndex()
{
$this->render('index');
}
}
the default page shows when i go to http://localhost/dev/calculator but the inner pages in the modules doesn't.
here is my regex
'<view:(about|terms|faq|privacy)>' => 'site/page',
'<action:(contact|login|logout)>' => 'site/<action>',
'<fileName:(calc1|calc2|calc3)>' => '/calculator/default/page/view/<fileName>',
'<action:(registration|create)>' => 'user/<action>',
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
any idea what i'm doing wrong?
In order to use the page action your urls should resolve to /<module>/<controller>/page/view/<fileName> where fileName is calc1, calc2 etc. Therefore in your case your rules should be:
'<fileName:(calc1|calc2|calc3)>' => '/calculator/default/page/view/<fileName>',
Reference: http://www.yiiframework.com/wiki/22/how-to-display-static-pages-in-yii/
First you need to define your action in your calculator controller:
class CalculatorController extends Controller
{
public function actionCalc1()
{
$this->render('calc1');
}
public function actionCalc2()
{
$this->render('calc2');
}
public function actionCalc3()
{
$this->render('calc3');
}
}
Of course you've to change your view paths and then you should see something when you type into your browser <your domain>/calculator/calc2.