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.
Related
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.
its gonna be a fully theoretical thread.
Let's talk about change arrays to specially object's, to compare and work on it.
For example we have a EntityClass, EntityInterface, SomeRepository, SomeManager, SomeCommand.
Entities is a clear object, example:
class EntityClass implements EntityInterface {
public $name;
public funtion getName() {
return $this->name;
}
public function assign($data) {
$this->name = $data['name'];
}
...
}
Repository ofc. have method's to save object in source and get it from source.
Manager have all of 'busines logic', it can modify a entities using command pattern, one command for one properties, so a busines logic for all properties its store in separate's command's, and manager fire it's.
Now, in manager start all logic and fun.
Little shorthand:
Repository create new entity by getOne().
We create a new instance of manager and pass by constructor some dependencies like array of data from controller and Entity.
Array of data have information about changes, example: ['name' => 'New name']; mapper resolve what command should have been fired by manager for given array.
Manager execute all commands for this request and passing raw array to each command.
Now, why we passing a array ? when we are in OOP, why don't use special variation of EntityClass ??
Now let's add a new interface EntityProposeInterface, and change a raw array to class implements this interface, and pass them.
for example we can add to SomeManager speciall method to morph entity like this (PHP7+):
class SomeManager {
public function morph($entity) {
$buff = new class extends EntityClass implements EntityProphoseInterface{};
$buff->assign($entity->toArray());
return $buff;
}
And now we have a EntityProphose lets make some changes on it, now manager change a EntityProphose from raw data array, and all commands and other method's in our code work's on object kinde EntityProphose instead of array.
But ofc. our repository cant save object's instance of EntityProphoseInterface.
And that's all...
Is there some design pattern name ? or something like this ?
Or this idea is very bad ?
I hope it's clear for all of you, if not please ask.
Any ideas ? advice ?
What makes me go crazy is why you would implement a function to assign values from an array to the whole class instead of just using regular assignments:
$entity->name = name;
$entity->age = age;
Perhaps you want to simplify this code... but what's the point of using arrays?
Your manager class (actually, a service) should provide functions with either parameters corresponding to whatever entity you want to modify or operate with, or just receive a DTO or the entity itself.
In the other hand, if you want to centralize how DTOs or entities are mapped one to another, maybe you need to implement object mappers:
class EntityMapper {
public function mapCustomerToCustomer($customerA, $customerB) {
if($customerA->name != null) {
$customerB->name = $customerA->name;
}
// and so on...
}
}
...and you can inject it wherever you want to map entities. For example, a CustomerService might look as follows:
class CustomerService {
function __construct($entityMapper) {
$this->entityMapper = $entityMapper;
}
public function update($customer) {
$customerToUpdate = $this->repository.getById($customer->id);
$this->entityMapper->mapCustomerToCustomer($customer, $customerToUpdate);
$this->repository.addOrUpdate($customerToUpdate);
}
}
In my MVC, I populate forms with data from the model using an array. I am using PDO and fetch associative array this gives an array of fields that I can use to populate the view.
When the user requests a new record, the array is empty and unless I explicitly test the value of every variable before use, the script falls over with a non-existant index.
My approach previously was, for new records, to setup an empty array and pass this to the view. This is fine for simple forms, but when the view needs to use 10-15 variables this is a bit tedious.
What is the preferred method of doing this?
Should my view test every field before use, or should I create the empty array. If so, should the empty array be created in the controller or the model?
As an example, If the user wants to edit an existing record, the controller passes the ID number to the model and asks it for an array of current values for the record.
The controller passes this array to the view which then incorporates the values into a form for editing.
In contrast, when the controller receives a request for a new record, it knows there is no point in asking the model for the record because its new. It can't just call the view because the passed array does not contain the keys required by the form.
My current approach to this is to initialise an array of empty keys but this seems tedious, time wasting and prone to error as it needs to be maintained if the model changes.
eg:
$this->view->class['Code']=NULL;
$this->view->class['Description']=NULL;
$this->view->class['Image']=NULL;
$this->view->class['Judge']=NULL;
$this->view->class['Entries']=NULL;
$this->view->class['Absentees']=NULL;
etc
There has to be a better way?
This is kind of a band-aid for poor MVC design, but you could use a function like this,
function idx(&$array, $index = 0, $default = null) {
return isset($array[$index]) ? $array[$index] : $default
}
In the view if you normal did $class['Description']
you would now use idx($class, 'Description')
You should not have to pass each attribute of your model individually. Instead, just pass the entire model object and access it from the view. Example:
$this->view->entry = $myModelInstance; where $myModelInstance is an instance of Entry or whatever class has the Code, Description, Image, Judges, etc attributes.
Then in the view, use something like $entry->Description
In my opinion setting propper defaults is a model issue.
Imagine a class like this:
class Car {
protected $color;
protected $vendor;
public function __construct($color, $vendor) {
$this->color = $color;
$this->vendor = $vendor;
}
public static function getDefault() {
return new Car('unknown', 'unknown');
}
}
However. You told that you use arrays. You'll find a similar way for arrays. I would suggest to write a function like getDefaultWhatever() and place it in model part of code.
Even though there's some discussions regarding this issue I wanted to check on certain example what would be the best approach.
Instead of using existing solutions I created my own persistence layer (like many do)
So my approach is also in question here.
For every table in db I have model class that has appropriate getters and setters and some mandatory methods. I also created only one generic DAO class that handles all types of model objects.
So, for example to save any model object I instantiate genericDAO class and call save method that I pass model object as attribute.
Problem is that in runtime genericDAO class doesn't know whitch model object it gets and what methods (getters and setters) exist in it, so I need to call mandatory model class method that retrieves list of attributes as multiple string array.
For example for every attribute there's array(table_column_name,attribute_name,is_string).
When I call save function it looks like this:
public function save(&$VO) {
$paramArray = $VO->getParamArray();//get array of attributes
$paramIdArray = $paramArray[0]; //first attribute is always id
/*create and execute getId() and store value into $void to check if it's save or update*/
eval('$voId = $VO->get'.ucfirst($paramIdArray[1]).'();');
...
Currently I'm using eval to execute those methods, but as it is well known eval is very slow.
I'm thinking of changing that into call_user_func method
Something like:
$voId = call_user_func(array($VO, 'get'.ucfirst($paramIdArray[1])));
But also there's other solutions. I can maybe use something like this $method = 'get'.ucfirst($paramIdArray[1]));
$voId = $VO->$method();
or else
$method = 'get'.ucfirst($paramIdArray[1]));
$voId = $VO->{$method}();
What would be the best way?
First of all, there's no need to pass references like you are doing. You should give this a read to try to understand how PHP handles object references.
So public function save(&$VO) { should become public function save($VO) {.
Second, there is no need to use eval (in fact, it's better not to because of speed, debugability, etc). You can't stack-trace an eval call like you can a dynamic one.
Third, call_user_func is all but useless since PHP supports dynamic variable functions. Instead of call_user_func(array($obj, $method), $arg1), just call $obj->$foo($arg1). The call_user_func_array function is still useful since it supports variable length arguments and supports passing references.
So, ultimately, I would suggest this:
$method = 'get' . ucfirst($paramIdArray[1]);
$voId = $VO->$method();
Note that there's no need to call method_exists, since it may be callable and not exist due to __get magic method support...
I normally would use:
$method = 'get'.ucfirst($attribute);
if(method_exists($obj, $method){
$obj->$method();
}
But unless there is a very good reason i would just return a key => value array from getParamArray. And operate on that instead of using the getters...
I have a class that performs database operations and returns results (array, true, false). And I have an another class that creates JSON string by using this class in its constructor.
Can we say this class is an Adapter? Or simply wrapper or ...
Class Db
{
public function getRows($params)
{
//...
}
}
Class DbAdapter
{
private $_dbh;
public function __construct($dbh)
{
$this->_dbh = $dbh;
}
public function getJson()
{
return '{"key": "foo", "key2": ' . json_encode($this->_dbh->getRows($params)) . '}';
}
}
Thanks
Id say its more of Decorator... http://en.wikipedia.org/wiki/Decorator_pattern
From: http://www.fluffycat.com/PHP-Design-Patterns/Adapter/
Adapters are helpful if you want to use a class that doesn't have quite the exact methods you need, and you can't change the orignal class. The adapter can take the methods you can access in the original class, and adapt them into the methods you need.
I would say your code qualifies as an adapter if you plan to add methods to supplement another class.
I don't think you can pick one pattern and say that's the one being used here. Here's my little list of patterns I see in your example. Feel free to comment.
Delegation because the DBAdapter class delegates the task of getting the actual rows to the DB class.
Decorator because the DBAdapter class decorates the DB class with additional functionality - that of spitting the output in JSON.
Adapter/Wrapper if you think that it allows other client to access your database rows that only understood JSON or XML or some other format.
But if we had to pick one, I'd say Adapter. It takes the data in the form of PHP data structures and converts it into a JSON representation.