What would be the Urlmanager rule for changing a url like site/product?name=[name] to product/[name]?
I tried
'<action:\w+>' => 'site/<action>',
'product/<id:\d+>' => 'product',
But it gives me a 404
Before answering to your question a short explain what is wrong.
You try to pass alpha chars to an action that handles only integers.
The rule 'product/<id:\d+>' indicate that the url should be like:
product/1
product/777
product/8888
The regular expression \d+ limits this url part to an integer.
Answer
For url like product/[name], you should add this pattern:
//...
'product/<name:[\w]+>' => 'product/item',
//...
Where 'product/<name:[\w]+>' will match any url like:
product/any
product/alpha
product/productname
The <name:[\w]+> will be the parameter that will hold matched url part and and creates a variable named $name that will contain only the alpha chars, due the regular expression [\w]+. This variable will be passed to controller action.
And 'product/item' is the controller / action that will handle request, in this example is ProductController and actionItem with parameter $name.
Now in ProductController you need to add an action:
//...
/**
* Handle products by name
* #param string $name
*/
public function actionItem($name) {
// ... do stuff here
}
//...
More information can be found here Yii2 Routing and URL Creation.
Related
The attributes of laravel modal are named using underscore (_), for example :
first_name
but attributes of javascript objects are named with camelCase:
{ firstName: "..." }
And this presents a conflict, is there a solution to resolve it ?
Try to use Laravel eloquent resource pattern will do that for You.
Check this helpful documentation.
https://laravel.com/docs/8.x/eloquent-resources
Like Zrelli Mjdi mentioned it's done with Resource Collections.
I did not find a way to let this resources transform the result recursively for nested JSON-Objects, so I created a middleware (see the github-gist) for this, which should take a rather heavy toll on performance. So use it sparsely.
I'd use this middleware only temporary if your frontend demands camel-case properties. In the long run I'd modify my migrations to use camel-case fieldnames. This should, according to this reddit-thread, be possible and won't affect performance like my middleware.
Edit: The code in the gist had a bug which is now fixed.
This is about how it's done with Resource-Collections and non-nested JSON-Results:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class MyResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'userId' => $this->user_id,
'createdAt' => $this->created_at,
];
}
}
in the controller:
public function myControllerMethod(Request $request)
{
// ...
return MyResource::collection($logs)
}
In laravel we can format our json response from resource class as seen below
class ProductsResource extends JsonResource
{
public function toArray($request)
{
return [
'id'=> $this->product_id ,
'code'=> $this->product_code,
'shortdescription'=> $this->product_short_description,
'image'=> $this->product_image,
];
}
}
But when returning resources collection i can't format my collection error Property [product_id] does not exist on this collection instance
class ProductsResource extends ResourceCollection
{
public function toArray($request)
{
return [
'id'=> $this->product_id ,
'code'=> $this->product_code,
'shortdescription'=> $this->product_short_description,
'image'=> $this->product_image,
];
}
}
thanks.
It's because ResourceCollection expects a collection of items instead of a single item. The collection resource expects you to iterate through the collection and cannot perform single enitity routines directly from $this (as it's a collection).
See resource collections in documentation
What you are probably looking for is to cast a custom mutation which examples can be found here:
See custom casts in documentation
Look/search for Value Object Casting. It is explained thoroughly how to mutate attributes on get and set, which is probably better than a resource collection (if this is the only thing you wish to do with it). This will modify the collection immediately and saves you from having to manually instantiate the resource collection every time you need it (as you are modifying at model level).
Coming from the docs:
Value Object Casting
You are not limited to casting values to primitive types. You may also cast values to objects. Defining custom casts that cast values to objects is very similar to casting to primitive types; however, the set method should return an array of key / value pairs that will be used to set raw, storable values on the model.
But to get back on topic...
If you dump and die: dd($this); you will see there is an attribute called +collection
In case you wish to transform keys or values, you have to iterate through $this->collection in order to transform the collection values or keys to your requirements.
As you can see in the parent class Illuminate\Http\Resources\Json\ResourceCollection the method toArray() is already a mapped collection.
In which you can see it's pointing to $this->collection
/**
* Transform the resource into a JSON array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return $this->collection->map->toArray($request)->all();
}
You could use something like the following. And update the item/key values within this collection map.
return $this->collection->map(function($item, $key){})->toArray();
if you wish to transform the values before returning it to an array.
Or a simple foreach like this (have not tested it and there are far better ways of doing this) But for the sake of sharing a simple-to-grasp example:
$result = [];
// Map the associations to be modified
$resultMap = [
'product_id' => 'id',
'product_code' => 'code',
'product_short_description' => 'shortdescription',
'product_image' => 'image'
];
// Iterate through the collection
foreach ($this->collection as $index => $item)
foreach ($item as $key => $value)
$result[$index][$resultMap[$key]] = $value;
return $result;
I have an attribute of the model which should be modified after it's loaded from the database.
I could extend the afterFind method, which could the convert the varchar value to a php array. So it works find.
But when the model is loaded I have no idea how to convert that varchar to the php array.
I have tried with rules but does not works:
[['languages'], 'each', 'rule' => ['string']],
or this one
[['languages'], 'safe'],
So this one works afterFind:
public function afterFind()
{
$this->languages = $this->convertToPHPArray($this->languages);
parent::afterFind();
}
By the way I have tried to extend the init or the __constructor method with this conversation, but no success, after load method the languages attribute is still a string instead of a php array.
If I understood your question, I think that you could use a property in the model:
public class Model {
public function getLanguagesArray()
{
return $this->convertToPHPArray($this->languages);
}
}
Then, use it:
$arr = $model->languagesArray;
I'm trying to implement the following:
Simple controller and action. Action should return response of 2 types depending on the request:
HTML in case of ordinary request (text\html),
JSON in case of ajax request (application\json)
I've managed to do this via a plugin for controller, but this requres to write
return $this->myCallBackFunction($data)
in each action. And what if I wan't to do this to whole controller? Was trying to figure out how to implement it via event listener, but could not succed.
Any tips or link to some article would be appreciated!
ZF2 has the acceptable view model selector controller plugin specifically for this purpose. It will select an appropriate ViewModel based on a mapping you define by looking at the Accepts header sent by the client.
For your example, you first need to enable the JSON view strategy by adding it to your view manager config (typically in module.config.php):
'view_manager' => array(
'strategies' => array(
'ViewJsonStrategy'
)
),
(It's likely you'll already have a view_manager key in there, in which case add the 'strategies' part to your current configuration.)
Then in your controller you call the controller plugin, using your mapping as the parameter:
class IndexController extends AbstractActionController
{
protected $acceptMapping = array(
'Zend\View\Model\ViewModel' => array(
'text/html'
),
'Zend\View\Model\JsonModel' => array(
'application/json'
)
);
public function indexAction()
{
$viewModel = $this->acceptableViewModelSelector($this->acceptMapping);
return $viewModel;
}
}
This will return a normal ViewModel for standard requests, and a JsonModel for requests that accept a JSON response (i.e. AJAX requests).
Any variables you assign to the JsonModel will be shown in the JSON output.
I activated the JsonStrategy in a ZF2 application and can get JSON output now using AcceptableViewModelSelector Controller Plugin.
It works only with the HTTP Request parameter Accept containing application/json.
Where is application/json defined as proper value for JSON output? (How) Can I define and use foo/bar instead?
Take a look here:
Zend\View\Strategy\JsonStrategy;
You can implement your own custom strategy in the same manner no problem. Much cleaner than hard coding into the controller as it can be reused.
Directly in the definition array of the accept criteria:
class SomeController extends AbstractActionController
{
protected $acceptCriteria = array(
'Zend\View\Model\JsonModel' => array(
'application/json', // <-- here
),
'Zend\View\Model\FeedModel' => array(
'application/rss+xml',
),
);
public function apiAction()
{
$viewModel = $this->acceptableViewModelSelector($this->acceptCriteria);
// Potentially vary execution based on model returned
if ($viewModel instanceof JsonModel) {
// ...
}
}
}