Yii2 findOne() exceeding execution time - yii2

I have a action that is doing a simple findOne($id) query and returning the one row from the database. This is exceeding maximum execution time. This method is being inherited by multiple classes where is works perfectly fine. I am not overriding any find() or afterFind() methods in the relevant Model.
public function actionGetone($id)
{
$classname = $this->model;
$model = new $classname;
return $model::findOne($id);
}
I don't get any errors and works as expected if I override the method with:
public function actionGetone($id){
$items = Job::find()->where(['id' => $id])->all();
return $items;
}
but as soon as I change it to return return $items[0]; id dies again with same Exceeded error.
Not sure if this is linked but I'm getting a Maximum execution time of 30 seconds exceeded error when the action is not mentioned in the behaviours() method and when it is added to access rule like below. But it gives me a Call to a member function checkAccess() on null error when i change access role to ['*']. I don't have authManager setup.
public function behaviors()
{
return [
'contentNegotiator' => [
'class' => \yii\filters\ContentNegotiator::className(),
'formats' => [
'application/json' => yii\web\Response::FORMAT_JSON,
],
],
'authenticator' => [
'class' => \yii\filters\auth\HttpBearerAuth::className(),
'only' => [ 'delete','patch','getone'],
],
'access' => [
'class' => \yii\filters\AccessControl::className(),
'only' => ['delete','patch','getone'],
'rules' => [
[
'actions' => ['delete','patch','getone'],
'allow' => true,
'roles' => ['#'],
],
],
]
];
}
I'd appreciate any ideas :)
Update
$items = Job::find()->where(['id' => $id]);
return $items;
Gives:
{
"sql": null,
"on": null,
"joinWith": null,
"select": null,
"selectOption": null,
"distinct": null,
"from": null,
"groupBy": null,
"join": null,
"having": null,
"union": null,
"params": [],
"where": {
"id": "3"
},
"limit": null,
"offset": null,
"orderBy": null,
"indexBy": null,
"modelClass": "common\models\Job",
"with": null,
"asArray": null,
"multiple": null,
"primaryModel": null,
"link": null,
"via": null,
"inverseOf": null
}

Found the problem, its to do with the recursive flag in the model's toArray() method. In my case, a user has jobs and a job has users specified in the model's fields() methods. Which is resulting in an infinite loop.
Add this to the relation's model to avoid the infinite loop:
public function toArray(array $fields = [], array $expand = [], $recursive = true)
{
return parent::toArray($fields, $expand, false);
}

Related

How To create update API in yii?

I created an api named valid which is not working. Postman says
"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 \"user/valid/1\".",
"code": 0,
"type": "yii\\base\\InvalidRouteException"
}
}
My controller name is user controller.
Here is my function
public function actionValid($id)
{
return 'example';
}
i called my route as /user/valid/1.
Any reason why this is happening?
This is how i solved this.
If you want to create your own apis in yii, You must follow these steps.
First add your route in UrlManager Like this
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => 'user',
'extraPatterns' => [
'POST valid/<id>' => 'valid',
'GET listing' => 'listing
]
],
],
]
Now, call your route like this users/valid/{your-id} and users/listing. Make sure to use a plural of your controller's name.
For example, a request of POST /users/data would mean accessing the actiondata function in your usercontroller.

How to also get custom fields of documents from Elasticsearch using Laravel Scout Elasticsearch Driver?

Below is the link to the Scout Elasticsearch Drive which I am using in my Laravel project.
https://github.com/babenkoivan/scout-elasticsearch-driver
I have database table users with the following columns:
id Integer
name String
created_at DateTime
Following the documentation on GitHub, my mapping in the User model looks like followings:
use Searchable; // using Searchable trait.
protected $mapping = [
'properties' => [
'name' => [
'type' => 'text',
'fields' => [
'raw' => [
'type' => 'keyword',
],
],
],
'created_at' => [
'type' => 'date',
'format' => 'dd-MM-yyyy HH:mm',
'fields' => [
'raw' => [
'type' => 'keyword',
],
],
],
// Custom field...
'created_date' => [
'type' => 'date',
'format' => 'dd-MM-yyyy',
'fields' => [
'raw' => [
'type' => 'keyword',
],
],
],
// Custom field...
'created_time' => [
'type' => 'date',
'format' => 'HH:mm',
'fields' => [
'raw' => [
'type' => 'keyword',
],
],
],
]
]
Below is the implementation of the toSearchableArray() function.
public function toSearchableArray()
{
$array = $this->toArray();
// Pre-format the custom fields before inserting them into Elasticsearch.
$array['created_date'] = $this->created_at->format('d-m-Y');
$array['created_time'] = $this->created_at->format('h:i');
return $array;
}
When using curl -X GET command I get the following results and of course with the custom fields.
"hits" : [
{
"_source" : {
"id" : 3,
"name": "John Doe",
"created_at": "31-12-2018 23:59",
"created_date": "31-12-2018", // My custom field.
"created_time": "23:59" // My custom field.
}
]
In my index() action in my controller, I query the data using the following code.
public function index()
{
return User::search("*")->get();
}
I get the records with only the original attributes, matching those columns in the database table as following.
[
{
"id" : 3,
"name": "John Doe",
"created_at": "31-12-2018 23:59",
}
]
Note that this is a JSON response provided by default by Laravel, responding to API calls. My custom attributes created_date and created_time do exist in Elasticsearch as well. How can I get them too as result? The reason I created these custom fields is to format the date and time on the server side beforehands so that my client-side does not need to worry about formating the date and time in the for-loop.
When I use User::search("*")->explain(); I do get my custom fields as well in the hits.hits._source{} object.
This should give you results directly from Elastic Search without matching up with your related models. I think thats the reason why your custom fields get lost.
public function index()
{
return User::search("*")->raw();
}
Documentation

return html in rest api yii2

I have a table country
CREATE TABLE `country` (
`code` CHAR(2) NOT NULL PRIMARY KEY,
`name` CHAR(52) NOT NULL,
`population` INT(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `Country` VALUES ('AU','Australia',18886000);
INSERT INTO `Country` VALUES ('BR','Brazil',170115000);
INSERT INTO `Country` VALUES ('CA','Canada',1147000);
my model:
namespace app\models;
use yii\db\ActiveRecord;
class Country extends ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'country';
}
/**
* #inheritdoc
*/
public static function primaryKey()
{
return ['code'];
}
/**
* Define rules for validation
*/
public function rules()
{
return [
[['code', 'name', 'population'], 'required']
];
}
}
My controller:
<?php
namespace app\controllers;
use yii\rest\ActiveController;
class CountryController extends ActiveController
{
public $modelClass = 'app\models\Country';
}
?>
and this is my config in urlManager
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => 'country',
'tokens' => [
'{id}' => '<id:\\w+>'
]
]
],
],
the webservice works perfectly but I need return a html with this dates.
For example, using Postman http://localhost/basic/web/countries/AU
actually return
{
"code": "AU",
"name": "Australia",
"population": 24016400
}
But I want that return html in the json content, for example:
{
"content": "<html>
<body>
<h1>AU</h1>
<p>Name: Australia</p>
<p>Population: 24016400</p>
</body>
</html>"
}
Is possible?
Thank you

cakephp 3 rest errors return as html CrudJsonApi

I'm adding a REST API onto an existing cakephp codebase.
It is working as I expect. I hit /api/v1/contacts/12312 and get json data back for contact 12312. If put an id of a contact that doesn't exist then I get the html 404 page error rather than json.
This happens internally on the contacts->get($id) line.
In the api app controller I have
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Crud.Crud', [
'actions' => [
'Crud.Index',
'Crud.View',
'Crud.Add',
'Crud.Edit',
'Crud.Delete'
],
'listeners' => [
'CrudJsonApi.JsonApi',
'CrudJsonApi.Pagination', // Pagination != ApiPagination
'Crud.ApiQueryLog',
],
'Error' => [
'errorLevel' => E_ALL,
'exceptionRenderer' => 'CrudJsonApi\Error\JsonApiExceptionRenderer',
'skipLog' => [],
'log' => true,
'trace' => true,
],
]);
$this->Crud->config(['listeners.jsonApi.exceptionRenderer' => 'CrudJsonApi\Error\JsonApiExceptionRenderer']);
$this->setJsonResponse();
}
public function beforeRender(Event $event)
{
$this->RequestHandler->renderAs($this, 'json');
$this->response->type('application/json');
$this->set('_serialize', true);
}
I thought using the JsonApiExceptionRenderer 404 errors would be handled with json output.
Also the Pagination works but the Pagination data isnt returned with the response...
{
"viewVar": "crmContacts",
"crmContacts": [
{
"id": 1,
"lastname": "testname1"
},
{
"id": 2,
"lastname": "smith"
}
],
"success": true
}
Any ideas?
Thanks

manually convert input parameters in type instance

I have some problem to save date value in varchar field.
I'm trying to convert an input date (like 17/06/2015) to an Time instance and then store it in my database as 2015-06-17
here an exemple of the entity which i want to save
(int) 1 => object(App\Model\Entity\Valeur) {
'new' => true,
'accessible' => [
'*' => true
],
'properties' => [
'caracteristique_id' => (int) 51,
'caracteristique' => object(App\Model\Entity\Caracteristique) {
'new' => true,
'accessible' => [
'*' => true
],
'properties' => [
'id' => (int) 51,
'type' => 'Date'
],
'dirty' => [
'id' => true,
'type' => true
],
'original' => [],
'virtual' => [],
'errors' => [],
'repository' => 'Caracteristiques'
},
'valeur' => object(Cake\I18n\Time) {
'time' => '2015-06-03T10:28:53+0000',
'timezone' => 'UTC',
'fixedNowTime' => false
}
],
'dirty' => [
'caracteristique_id' => true,
'caracteristique' => true,
'valeur' => true
],
'original' => [],
'virtual' => [],
'errors' => [],
'repository' => 'Valeurs'
},
After save i only got 17/06/2015 in my database
I would just save as a datetime and format when using it in a view, but what you could do is before saving (either in the beforeSave or in the Controller/model) altering the value to match whatever date you want
Assuming that your entity is called valeur and the field where you store your date is also valeur:
$valeur->valeur = $valeur->valeur->format('Y-m-d');
Well i found an answer in timestamp behavior so i did that in my entity:
protected function _setValeur($valeur)
{
if (isset($this->caracteristique)) {
if ($this->caracteristique->isDate()) {
try {
if (!($valeur instanceof Time)) {
$valeur = Time::createFromFormat('d/m/Y', $valeur);
}
return $valeur->format('Y-m-d');
} catch (\InvalidArgumentException $e) {
return null;
}
}
}
if (empty($valeur)) {
$valeur = null;
}
return $valeur;
}
protected function _getValeur($valeur)
{
if (isset($this->caracteristique)) {
if ($this->caracteristique->isDate()) {
try {
return Time::createFromFormat('Y-m-d', $valeur);
} catch (\InvalidArgumentException $e) {
return null;
}
}
}
return $valeur;
}
protected function _getDate()
{
if (isset($this->caracteristique)) {
if ($this->caracteristique->isDate()) {
return !is_null($this->valeur) ?
$this->valeur->format('d/m/Y') : $this->valeur;
}
}
return null;
}
the field valeur is typed dynamically referenced by the caracteristic association so even if in my db the valeur's field is a varchar in the app it can be a date, string or numeric.
In this case i test the type then i just retrieve Time object on find and i set to date string with the format 'd/m/Y' when i'm saving.
i also create a virtual property which give me formated date