I have a table posts and this table has some columns like pst_id, pst_title, pst_content, pst_category_id and like that. I wanna represent this fields with some better names on json output, actually I'm trying to remove the prefix pst_ from column names.
I tested multiple ways. At first I tried to make an alias for this columns on DB layer, for example Post::select(['pst_id as id'])->get(). This idea generally is awful, because it makes the column names inconsistent across of the software (each developer may have a convention to naming a field). So I insist to find a way for naming the columns on model layer.
The next solution was for using Accessors and Mutators. Although it covers the problem of previous way, but it's really hard to implement 20 methods for each model! 20x100 ~ 2000 methods!!! :/
The last solution which I tested was about using the mappable feature of https://github.com/jarektkaczyk/eloquence. It's really good, I can put all old fields to $hidden property and add new ones to $appends for showing on output. But this solution also have a problem. If I add all new fields to $appends, when I use select statement for choosing some columns, the non-selected columns will be showed on output with a null value :|. Well, I tried to override mappedselect and parseMappings methods on a base model for adding new names to $appends dynamically, but it doesn't satisfy me. In fact it becomes very tricky on using and I'm not sure that the team can accept it and use it easily.
So that's the problem: "Is there a way for renaming the name of columns on output for eloquent?". GoLang has a very good feature which is called Struct Tags. You can define some tags for your structure, for example like this:
type Post struct {
Pst_id int `json:"id"`
Pst_title string `json:"title"`
Pst_content string `json:"content"`
}
And when you produce a json for a Post structure with json.Marshal, based on tags, it gives you a json like this:
{
"id": 23,
"title": "Custom Field Tags for Eloquent",
"content": "I tried a lot of things, but they are hard. I'm a programmer so I'm lazy! What can I do?",
}
I think we don't have something like this in the php world, but is there any way to use the idea behinds doctrine's annotation for implementing something like tag structure in Go?
Any comments and idea are welcome!
First step would be to override a couple methods on those models. The first method is the getAttribute() which is called when you access an attributed of a model so you can access it. You would want to be able to access the attribute without the pst_ prefix so you would do:
public function getAttribute($key)
{
if(array_key_exists($prefixedKey = 'pst_'.$key, $this->attributes)) {
return $this->attributes[$prefixedKey];
}
return parent::getAttribute($key);
}
Then to make sure the keys don't have the prefix when casting to json you would override the attributesToArray() method which is what is called when outputting json and will also respect your $hidden, $visible, $casts and $dates arrays. That would be something like:
public function attributesToArray()
{
$attributes = parent::attributesToArray();
$mutated = [];
foreach ($attributes as $key => $value) {
$mutated[preg_replace('/^pst_/', '', $key)] = $value;
}
return $mutated;
}
To implement those you can extend the Model class with an abstract class that implements those methods and have your classes extend that base class or create a trait with those methods and have your classes implement that trait.
I would probably use Fractal Transformer from the League.
You basically create a mapping class and apply it to the collection.
The Transformer class would look like this
<?php
namespace App;
use App\Post;
use League\Fractal;
use League\Fractal\TransformerAbstract;
class PostTransformer extends TransformerAbstract{
public function transform(Post $post)
{
return [
'id' => (int) $post->pst_id,
'title' => $post->pst_name,
'content' => $post->pst_uuid,
];
}
}
Then in your controller or where ever you transform the collection.
$posts = Post::all();
$manager = new Manager();
$manager->setSerializer(new CursorSerializer());
$resource = new Collection($posts, new PostTransformer());
$formattedCollection = $manager->createData($resource);
The docs are pretty good and it is pretty straight forward to implement it.
Related
I've been looking for a Laravel transformer that will format the fields both ways. Meaning I transform it when I return it to the client and then transform it too before saving it again to the database.
I know I can do this already using Fractal's Transformer but i'm looking for a way (either code or 3rd party library) for the transforming to be automatic. Right now i'm doing it like this for the save functionality:
$data = transform($request->all()); //transforms the input into database field names
$person = Person::create($data);
return response()->json(transform($person), 200); //before returning I transform it to field names needed by client
I'm using a legacy database so the fields I used in the frontend and the database doesn't match. It's also a big app so I think it would be better if there was a way to use a Trait or maybe something like an inheritance from the model level instead of doing the code above from a controller, repository, service.
Use accessors and mutators to get and save the data into DB, while use $maps property on Model to change the fields names for the front end.
class User extends Model
{
protected $maps = ['name_in_db' => 'name_on_frontend'];
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
public function setFirstNameAttribute($value)
{
$this->attributes['first_name'] = strtolower($value);
}
}
If I have a model that needs to have a property that is an array of different models. Is there an eloquent method or way to handle this kind of problem?
eg.
I have a Feature model that needs a method that gets an array of objects that are from different models.
class Feature extends Model
{
public function getArrayOfDifferentObjects()
{
$array_of_objects=array();
???? ELOQUENT to get objects from different models ????
return $array_of_objects;
}
}
I have a feature_model_connections table with the following:
feature_id
featured_model_id
featured_model_type
The featured_model_type value would be a string denoting the model type.
The model_id would be a foreign key of the relevant model's table.
However I can't see how you would be able to use eloquent to return data for the getArrayOfDifferentObjects method in features model.
Any pointers would be much appreciated. Many thanks, J
What you are describing there, is basicly a Polymorphic Relations, which can handle these cases, and making fetching them easy, instead of i'm making a made up case, read the documentation, it is well written, under the section Polymorphic Relations. https://laravel.com/docs/5.1/eloquent-relationships#polymorphic-relations
Within your scope right now, you can do something like this.
public function getArrayOfDifferentObjects()
{
$objects = [];
$features = DB::table('feature_model_connections')
->select('feature_id', 'featured_model_id', 'featured_model_type')->get();
foreach($features as $feature)
{
$type = '\\App\\' . $feature->featured_model_type; //App is you app namespace
$model = $type::find($feature->featured_model_id);
if($model)
$objects[] = $model;
}
return $objects;
}
The basics of this, is you can define different types, with the app namespace seed, from there staticly call them, which will access the predefined type in your database table, then find the element and add it to the array. With that said, this is done as of the top of my head, no compile check, not ranned in Laravel, but it should pretty much get you the idea of what to do, with that said, if you can change your structure, go with the Polymorphic Relations, it is really awesome.
In eloquent we can cast multiple attributes to Carbon instances by specifying the dates in an array:
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
I have multiple attributes that I want to apply the same presentation logic to and instead of using multiple accessors (one method per attribute), I was wondering if it were possible to set up an array like the aforementioned dates array by which a particular method can be applied to.
So instead of having multiple accessors, like this:
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
public function getLastNameAttribute($value)
{
return ucfirst($value);
}
I could instead have an array like this:
protected $ucfirst = ['first_name', 'last_name'];
and then the ucfirst() function would be applied to the array by creating a method to do this.
Didn't see this in the docs and couldn't find anything from a search, but I may have been searching for the wrong thing.
Any solutions solely using eloquent would be great, I don't really want to pull in another package to do it if I can avoid it. Thanks!
Edit
Going to investigate how the $dates to Carbon works and try a similar approach, perhaps this is a starting point:
https://github.com/laravel/framework/blob/5.2/src/Illuminate/Database/Eloquent/Model.php#L2895
I don't know such possibility.
But you can encapsulate repeating functionality to some method and use this method in your accessors.
Would it make more sense in the following situation to create a new object type instead of using an array:
I have a simple structure with two values: Name & Age
$ages = array(
array(
'name' => 'bill',
'age' => 22
),
array(
'name' => 'bob',
'age' => 50
),
// etc
);
The problem is that this structure is generated in one class and passed through a controller and then used in another class.
Therefore it feels like those two classes are tied together as one must know the array keys of this structure that is generated in another.
Is there any design pattern that solves this?
I don't think you need (of even if there is) a design pattern for generating an object/data structure in one class, and consuming it in another. It is the basic premise of working with classes. Also, as alfasin mentioned, working with objects are neater than arrays. Also in the future, you could have better interactions with other objects, if such a need arises.
I would go all the way and define a Person model class. Something like this
Class Person {
protected _age;
protected _name;
public function __construct($name = null, $age = null) {
if ($name) setName($name);
if ($age) setAge($age);
}
public function getName() {
return $this->_name;
}
public function setName($name) {
return $this->_name = (string) $name;
}
public function getAge() {
return $this->_age;
}
public function setAge($age) {
return $this->_age = (int) $age;
}
}
You can then use this class to create your datastructure as follows:
$persons = array(new Person('bill', 22),new Person('bob', 50));
This array can then be passed by your controller and used like this in the view:
foreach($persons as $person) {
echo $person->getName();
echo $person->getAge();
}
This design pattern is called MVC (Model View Controller) and very popular and well documented, although interpretations my differ.
This might look like overkill for your simple structure, but it will save you a lot of time when having to extend your code in the future.
(the code is not tested, but i think it should work just fine)
Since it's a simple structure you can work with it, but in general it's recommended to work with objects. If you'll want to add fields in the future, add levels (nested arrays) - maintenance will be easier as you're program will be more modular and less coupled:
// I - easier to use
$bill_age = $ages->get_age('bill');
// function get_age() is implemented in the class which
// makes you code easier to maintain and easier to understand
// II - this implementation is dependent on the structure of $ages
// if you'll change $ages - you'll have to change all the uses:
$bill_arr = $ages[0];
$bill_age = $bill_arr['age'];
Further, if you'll have calls like II on different places in the code, changing $ages structure will result in changing all these places, while if you implement I - you have only one place in the code to change (the implementation of get_age($name) inside the class).
I think you can have one class which will contain the Keys for this structure and then both class will share this class to get key instances.
In that way you won't have to keep track of keys in both the classes. Moreover, anytime you can add more keys without changes much here n there. Less Coupling and More Flexibility.
I have a simple problem where I often return CRUD type Ajax requests with array serialized versions of Doctrine 1.2 models. I'd love to be able to simply return the toArray() method after the execute() result, however, this will display data about my models that I don't wish to expose. A simple example is on my user model the password and salt get displayed. While I realize those are already hashed values, it's something I'd rather not return as a JSON response.
I've poured over the Doctrine 1.2 manual, but did not find anything that offered the type of functionality I'm looking for. I realize I can iterate over the result to manually unset() the columns I wish to hide, but I'm hoping a more native solution is out there that I've overlooked.
Why don't you build your own toArray() ?
If you want to do that, you will have to extends the sfDoctrineRecord class that inherit from all Base* class. It is describe in the doc.
You have to put the configureDoctrine() inside config/ProjectConfiguration.class.php.
Then you will have a class like that:
class myDoctrineRecord extends sfDoctrineRecord
{
}
So you can easily add your custom toArray() here:
class myDoctrineRecord extends sfDoctrineRecord
{
public function toArray($deep = true, $prefixKey = false, array $excludeFields = array())
{
// do every thing like the original toArray
// but when a column match one entry in $excludeFields, don't add it
}
}
So, when using the toArray() method with an array of fields for the third parameters, they will be excluded from the result.