How to convert Laravel DB data into a suitable JSON form to use it in vuejs - json

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)
}

Related

Can the Yii2 RESTful API return the JSON data in camelcase format?

Is it possible to configure a Yii2 RESTful API to return JSON responses in camelcase format instead of snakecase without creating the column names that way? I can't find any mention in the docs or elsewhere.
Not possible by configuration alone, to achieve this you need to overwrite the fields() method
You would need to do this for each model your api exposes
class SomeModel extends \yii\db\ActiveRecord
{
/**
* #return array|false
*/
public function fields() {
// what you want is to rename the keys of parent::fields()
$formattedFields =[];
foreach (parent::fields() as $key => $name){
$formattedFields[Inflector::variablize($key)] = $name;
}
return $formattedFields;
}
}
fields() method is basically a way to add, remove, rename or redefine fields.
The documentation page i've linked goes into more details about use-cases and other examples

Yii2 how can an model attribute is modified after load method? (like the afterFind method)

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;

Yii2 field accessed only via magic method

/**
* This is the model class for table "hashtag".
*
* #property string $text
*
* #property TweetHashtag[] $tweetHashtags
* #property Tweet[] $tweets
*/
class Hashtag extends ActiveRecord
{
.........
public function getTweetHashtags()
{
return $this->hasMany(TweetHashtag::className(), ['hashtag_text' => 'text']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getTweets()
{
return $this->hasMany(Tweet::className(), ['id' => 'tweet_id'])->viaTable('tweet_hashtag', ['hashtag_text' => 'text']);
}
}
When I do in some component
$hashtags = Hashtag::find()
->with('tweets')
->where(['text' => $hashtagText])
->all();
foreach($hashtags as $hashtag)
{
print_r($hashtag->tweets);
}
It`s working but why tweets - field accessed only via magic method and how can i fix it? And tweetHashtags working well.
Class Tweet have same relationship but public function getHashtags() working without this problem.
Your question is not clear. Each method on a Component class that start with get (like getName) can be accessed with property form (e.g. name). On special case, relations of Yii's ActiveRecord, if you access to relation by property form, you get results. In fact $this->tweets is a shorthand for $this->getTweets()->all().
P.S: On Yii2 Document http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#accessing-relational-data:
Note: While this concept looks similar to the object property feature,
there is an important difference. For normal object properties the
property value is of the same type as the defining getter method. A
relation method however returns an yii\db\ActiveQuery instance, while
accessing a relation property will either return a yii\db\ActiveRecord
instance or an array of these.
$customer->orders; // is an array of `Order` objects
$customer->getOrders(); // returns an ActiveQuery instance
This is useful for creating customized queries, which is described in the next section.

Using Fractal Transformer with ember-data

I am using PHP league's Fractal as the transformer for my API. However, I think I must be doing something wrong as the item transformer wraps everything in an array like it would a collection which is against the JSON API standard I believe.
So for a user with ID of one I get something like this:
{
"users":[
{
"id":1,
"firstName":"Jacob",
"surname":"Windsor",
}
]
}
When surely it should be this?
{
"users":
{
"id":1,
"firstName":"Jacob",
"surname":"Windsor",
}
}
I am using ember.js and this is causing problems with naming conventions.
I am using Laravel and in my userController I have something like this:
public function show($id)
{
$user = User::find($id);
return $this->respondItem($user);
}
Then in the apiController that everything extends from:
public function respond($response, $status = 200){
return Response::make($response, $status);
}
public function respondTransform($resource){
$fractal = new Fractal\Manager();
$fractal->setSerializer(new JsonApiSerializer());
return $this->respond($fractal->createData($resource)->toJson());
}
public function respondItem($data, $transformer = null, $namespace = null){
! isset($transformer) ? $transformer = $this->transformer : $transformer = $transformer;
! isset($namespace) ? $namespace = $this->namespace : $namespace = $namespace;
$resource = new Item($data, $transformer, $namespace);
return $this->respondTransform($resource);
}
I must be doing something wrong. The fractal docs have no examples specifically for items only collections so I am unsure what I have done.
So it seems that Fractal doesn't quite obey ember-data's conventions which is an annoying problem but very easily overcome using custom serialziers.
I have a psr-4 autoloaded file named CustomJsonSerializer which I have included in my ApiController class. If you follow the article on php league's site (posted above) its fairly easy to do. I have these two methods.
public function collection($resourceKey, array $data)
{
return array($resourceKey ?: 'data' => $data);
}
/**
* Serialize an item resource
*
* #param string $resourceKey
* #param array $data
*
* #return array
*/
public function item($resourceKey, array $data)
{
return [$resourceKey => $data];
}
You can see that the collection is responding as it normally would, i.e I haven't changed it. But the item method just responds without the extra array. Simple! You have to include all the other methods as well and I haven't got round to sorting out pagination but it should be fairly simple.
I hope this helps anyone wanting to use ember-data with Fractal. I highly recommend it, fractal has made my life so much easier. You could build transformers yourself but it makes it so much easier and more easily modified in the future.
Edit:
Please make sure you keep the $resourceKey in both the methods. You need to be using it and setting it when calling the transformer. |Ember-data requires a resource key.
Assuming your userController extends ApiController, you could simply do:
public function show($id)
{
$user = User::findOrFail($id);
return $this->setStatusCode(200)->withItem($user, new UserTransformer);
}
You do need to implement the UserTransformer class. If you need help with that, let me know in the comments.
I actually found that a much simpler adjustment of JsonApiSerializer did what I needed for Ember:
(I just took out the count($data) check)
<?php
namespace Acme\Serializer;
use RuntimeException;
use League\Fractal\Serializer\JsonApiSerializer;
class EmberSerializer extends JsonApiSerializer
{
/**
* Serialize the top level data.
*
* #param string $resourceKey
* #param array $data
*
* #return array
*/
public function serializeData($resourceKey, array $data)
{
if (! $resourceKey) {
throw new RuntimeException('The $resourceKey parameter must be provided when using '.__CLASS__);
}
return array($resourceKey => $data);
}
}

Where is defined the according type for the JSON output in Zend Framework 2?

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) {
// ...
}
}
}