Too many parameters for model creation in controller? - php

A have a lot of controllers where I must to save/create new models, it looks like this:
public Controller_Test extends Controller_Template {
if ($post = $this->request->post()) {
$model = ORM::factory('model');
$model->param1 = $post['Param1'];
$model->param2 = $post['Param26'];
$model->param3 = $post['Param31'];
$model->param4 = $post['Param13'];
$model->param5 = $post['Param2'];
$model->param6 = $post['Param35'];
$model->param7 = $post['Param10'];
$model->param8 = $post['Param22'];
$model->param9 = $post['Param3'];
$model->save();
}
}
Is it possible to unify (create a method) thats will save all array?
I know about $model->values($post)->create();, but still can't understand how really same it works, as u can see I have different keys of posted parameteres and this might be considered.
In many examples all the data assignemnts take place in controller, but they're really small, in my case I'll suppose to have a huge controllers with a lot of data assignment strings and it will be a bad style coding I think.

Whatever you do you need to map the key names in your $_POST variable to model property names.
$model_post_map = array(
'param1' => 'Param1',
'param2' => 'Param26',
'param3' => 'Param31',
'param4' => 'Param13',
'param5' => 'Param2',
'param6' => 'Param35',
'param7' => 'Param10',
'param8' => 'Param22',
'param9' => 'Param3',
);
$post_model_map = array_flip($model_post_map);
function rekey($arr, $map) {
$newarr = array();
foreach ($arr as $k => $v) {
if (isset($map[$k])) {
$newarr[$map[$k]] = $v;
}
}
return $newarr;
}
$modeldata = rekey($post, $post_model_map);
$model->values($modeldata);
You should name your form fields the way you do your models to reduce the impedance mismatch.
You should also use the second argument to $model->values() to restrict what a form can change.

Related

Why getPostParameters doesn't work on symfony 1.4?

I have a form in symfony 1.4 which are created for each competence. My forms was created with success. But when I try to save my form I can't. I go to see my action code and the function getPostParameters seem dosn't work. I use getParameterHolder to see what's wrong in my parameters but after I put the good value the getPostParameters function doesn't work.
This is what I get from getParameterHolder:
sfParameterHolder Object
([parameters:protected] => Array
(
[professionnal_competence] => Array
(
[rayon_competence3] => 24
[rayon_competence9] => 22
[rayon_competence19] => 32
)
[module] => professionnal_subregion
[action] => saveCompetenceRadius
)
)
And my function:
public function executeSaveCompetenceRadius(sfWebRequest $request) {
$user = $this->getUser()->getGuardUser();
$q = ProfessionnalCompetenceQuery::create()
->addSelect('pc.*')
->where('pc.professionnal_id= ?', $user->getId());
$res = $q->execute();
$values = $request->getPostParameters(['professionnal_competence']);
$test = $request->getParameterHolder();
var_dump($values); print_r($values); print_r($request->getParameterHolder());
exit;
foreach ($res as $professionnalCompetence) {
foreach ($values['professionnal_competence'] as $k => $val) {
if ($k == 'rayon_competence' . $professionnalCompetence->getCompetenceId()) {
$professionnalCompetence->setRayonCompetence($val);
$professionnalCompetence->save();
}
}
}
return $this->renderComponent('professionnal_subregion', 'competenceRadius');
// return "test";
//return $this->renderPartial('professionnal_subregion/competenceradius');
}
This is my form:
class ProfessionnalCompetenceRadiusForm extends BaseProfessionnalCompetenceForm {
public function configure()
{
unset($this['rayon_competence']);
$this->widgetSchema['rayon_competence'.$this->object->getCompetenceId()] = new sfWidgetFormSelectUISlider(array('max'=>50,'step'=>1));
$this->widgetSchema->setHelp('rayon_competence'.$this->object->getCompetenceId(),'en kilomètres');
$this->widgetSchema->setLabel('rayon_competence'.$this->object->getCompetenceId(),'rayon');
$this->setValidator('rayon_competence'.$this->object->getCompetenceId(), new sfValidatorInteger(array('max'=>50)));
}
}
Someone has an idea or can help me ?? Because I try lot of thing but without success. Thank in advance :).
I think the error hides in this line:
$values = $request->getPostParameters(['professionnal_competence']);
You're passing an array to a function that takes a string. Try removing the brackets around 'professionnal_competence'.
EDIT: Scratch that. getPostParameters takes no parameters. getPostParameter, on the other hand, takes two - the first of which is the field name - a string. So, your code should be:
$values = $request->getPostParameter('professionnal_competence');
The error is here:
$values = $request->getPostParameters(['professionnal_competence']);
The function sfWebRequest::getPostParameters doesn't actually take parameters.
You can either access this array with [...], or use getPostParameter, which allows "safe" deep access:
$val = $request->getPostParameter('a[b]');
// basically the same as, but with error checks:
$val = $request->getPostParameters()['a']['b'];

How to create data and return properly formatted json using ApiGility and RPC

I am using the RPC service of ApiGilty to return some data. I would like to double check whether or not this is the correct way of formatting and returning the data as I am not 100% sure of the correct process.
EDIT: To clarify
The data is being built from a number of entities:
main
main_extra
main_data
main_data_days
main_data_tiers
Is there a way to hit main and get all the sub entities? Currently I am building my data from scratch and returning an array.
My RPC Controller is as follows:
use My\Data\Controller\DataInterface;
use Zend\Mvc\Controller\AbstractActionController;
use ZF\ContentNegotiation\ViewModel;
class MyDataController extends AbstractActionController
{
const GENERAL_ERROR = 'api.rpc.my-data.my-data-controller';
public function __construct(
MyDataInterface $myData
)
{
$this->myData = $myData;
}
public function myDataAction()
{
$my_id = (int) $this->params()->fromRoute('my_id', 0);
if ($my_id == 0)
{
$data = $this->myData->getMyData();
} else
{
$data = $this->myData->getMyData($my_id);
}
$result = new ViewModel(array(
'data' => $data
));
return $result;
}
}
Now to create the data I am doing something like this:
public function getMyData( $my_id = null )
{
$returnArray = [];
$array1 = [
'key_1' => [1,2,3,4],
'key_2' => '123',
'key_3' => ['a','b','c']
];
$array2 = [
'key_1' => [1,2,3,4,5,6,7,8],
'key_2' => '123456',
'key_3' => ['a','b','c','d']
];
if ($my_id == 1) {
$array3 = ['some','or','other'];
} else {$array3 = []; }
$final_array = [
'data1' => $array1,
'data2' => $array2,
'data3' => $array3
];
$returnArray['data'] = $final_array;
$returnArray['success'] = 'true';
$returnArray['reason'] = '';
return $returnArray;
}
When checking with postman, I get the following:
Now since I have nothing to reference this against, my question is simply. Have I gone about this in the correct way and is this how the return code should be formatted?
Thanks!
Right now the Hal plugin is not used to render your result? You are responding a custom json object. Is this really what you want?
The response you currently return is not formatted according to HAL specifications. A proper HAL response should hold at least a _links key with a self href. It would be wrong to return this result with Content-Type headers set to application/hal+json. You should use application/json instead.
Here you can find documentation on how to respond HAL from an RPC-contoller.
I am not sure what you want to achieve but maybe you can be a bit more specific in your question so others can help out...
Doesn't look too bad, perhaps adhere to a standard such as jsend http://labs.omniti.com/labs/jsend or you could use hal-json, matthew weier o'phinney has a good blog post on this https://mwop.net/blog/2014-03-26-apigility-rpc-with-hal.html
Also you don't need to return a view model as you can just return an array and apigility will return JSON. You could also write a jsendViewModel if you go down that route.
Not exactly an answer but hope this helps you!

PHP Object, set multiple properties

Is it possible to set multiple properties at a time for an object in php?
Instead of doing:
$object->prop1 = $something;
$object->prop2 = $otherthing;
$object->prop3 = $morethings;
do something like:
$object = (object) array(
'prop1' => $something,
'prop2' => $otherthing,
'prop3' => $morethings
);
but without overwriting the object.
Not like the way you want. but this can be done by using a loop.
$map = array(
'prop1' => $something,
'prop2' => $otherthing,
'prop3' => $morethings
);
foreach($map as $k => $v)
$object->$k = $v;
See only 2 extra lines.
You should look at Object Oriented PHP Best Practices :
"since the setter functions return $this you can chain them like so:"
$object->setName('Bob')
->setHairColor('green')
->setAddress('someplace');
This incidentally is known as a fluent interface.
I would recommend you don't do it. Seriously, don't.
Your code is much MUCH cleaner the first way, it's clearer of your intentions, and you aren't obfocusing your code to the extent where sometime in the future someone would look at your code and think "What the hell was the idiot thinking"?
If you insist on doing something which is clearly the wrong way to go, you can always create an array, iterate it and set all the properties in a loop. I won't give you code though. It's evil.
You could write some setters for the object that return the object:
public function setSomething($something)
{
$this->something = $something;
return $this; //this will return the current object
}
You could then do:
$object->setSomething("something")
->setSomethingelse("somethingelse")
->setMoreThings("some more things");
You would need to write a setter for each property as a __set function is not capable of returning a value.
Alternatively, set a single function to accept an array of property => values and set everything?
public function setProperties($array)
{
foreach($array as $property => $value)
{
$this->{$property} = $value;
}
return $this;
}
and pass in the array:
$object->setProperties(array('something' => 'someText', 'somethingElse' => 'more text', 'moreThings'=>'a lot more text'));
I realise this is an old question but for the benefit of others that come across it, I solved this myself recently and wanted to share the result
<?php
//Just some setup
header('Content-Type: text/plain');
$account = (object) array(
'email' => 'foo',
'dob'=>((object)array(
'day'=>1,
'month'=>1,
'year'=>((object)array('century'=>1900,'decade'=>0))
))
);
var_dump($account);
echo "\n\n==============\n\n";
//The functions
function &getObjRef(&$obj,$prop) {
return $obj->{$prop};
}
function updateObjFromArray(&$obj,$array){
foreach ($array as $key=>$value) {
if(!is_array($value))
$obj->{$key} = $value;
else{
$ref = getObjRef($obj,$key);
updateObjFromArray($ref,$value);
}
}
}
//Test
updateObjFromArray($account,array(
'id' => '123',
'email' => 'user#domain.com',
'dob'=>array(
'day'=>19,
'month'=>11,
'year'=>array('century'=>1900,'decade'=>80)
)
));
var_dump($account);
Obviously there are no safeguards built in. The main caveat is that the updateObjFromArray function assumes that for any nested arrays within $array, the corresponding key in $obj already exists and is an object, this must be true or treating it like an object will throw an error.
Hope this helps! :)
I wouldn't actually do this....but for fun I would
$object = (object) ($props + (array) $object);
you end up with an stdClass composed of $objects public properties, so it loses its type.
Method objectThis() to transtypage class array properties or array to stdClass. Using direct transtypage (object) would remove numeric index, but using this method it will keep the numeric index.
public function objectThis($array = null) {
if (!$array) {
foreach ($this as $property_name => $property_values) {
if (is_array($property_values) && !empty($property_values)) {
$this->{$property_name} = $this->objectThis($property_values);
} else if (is_array($property_values) && empty($property_values)) {
$this->{$property_name} = new stdClass();
}
}
} else {
$object = new stdClass();
foreach ($array as $index => $values) {
if (is_array($values) && empty($values)) {
$object->{$index} = new stdClass();
} else if (is_array($values)) {
$object->{$index} = $this->objectThis($values);
} else if (is_object($values)) {
$object->{$index} = $this->objectThis($values);
} else {
$object->{$index} = $values;
}
}
return $object;
}
}

PHP using an array in a Validation Class

On my site I have my register page, where a user inputs his information to register to my site.
When he does the I process the user inputted information. Now when processing it I'm using arrays to facilitate the process, and not have me write the same code over and over. For example, here is how I gather the user information and check it using a method in my validation class.
$data = array(
$_POST['firstname'] => "First Name",
$_POST['lastname'] => "Last Name"
);
foreach($data as $userInput => $fieldName) {
$validation = new Validation();
$result = $validation->checkEmpty($userInput, $fieldName);
}
Now this all works for me on my page, I'm able to check to see if the user left something empty on the register with the method "checkEmpty", which also returns an array of the fields left empty. I didn't show the method because it's not part of my problem. Now, here's my question, I want to also check the length of the fields. What would be a good way for me to do this without rewriting things over and over? Would I have to do another array?
I'm thinking something like this, maybe?
$data2 = array(
$_POST['firstname'] => 30,
$_POST['lastname'] => 35
),
foreach($data as $userInput => $limit) {
$result = $validation->checkLength($userInput, $limit);
}
But where I get stumped is, if one the inputted fields is too long, how do return which one it was if in the array I passed through I don't have the field name to which it belongs? I thought of a multi-dimensional array, but I'm not sure if that will work.
I would structure the the array completely differently, something like:
$config = array(
'firstname' => array('notEmpty' = true,
'limit' => 30),
'lastname' => array('notEmpty' = true,
'limit' => 35)
);
Then I would create a method validate that looks like this:
public function validate($config, $data) {
$error = array();
foreach($data as $field => $value) {
$c = $config[$field];
if(array_key_exists('notEmpty', $c)) {
if(!$this->checkEmpty($value)) {
$this->addError($error, $field, 'Must not be empty');
}
}
if(array_key_exists('limit', $c)) {
if(!$this->checkLength($value, $c['limit'])) {
$this->addError($error, $field, 'Must not be longer than' . $c['limit'] . ' characters');
}
}
/*...*/
}
return $error;
}
private function addError(&$error, $field, $msg) {
if(!array_key_exists($field, $error)) {
$error[$field] = array();
}
$error[$field][] = $msg;
}
Then you just call:
$validation = new Validation();
$errors = $validation->validate($config, $_POST);
and $errors will contain all error messages per field. You just need to to loop over them and print them next to the field.
Of course this code can (and should be!) improved. E.g. dynamic lookup of validation methods).
That said, I highly recommend to have a look at and use a ready made validation classes such as Zend_Validate
I would think using the field names as keys for your array may be a better approach. Then yeah, use a multi-dimensional array something like this.
$data = array(
'First Name' => array($_POST['firstname'], 30),
'Last Name' => array($_POST['lastname'], 35)
)
You gloss over the validation class, but I think its important. For one it should be used statically if it doesn't have any state. Theres no reason to be constructing it every single loop iteration like that!
Validation::checkEmpty($x);
AS for the length checking, id suggest you keep fields in Validation something like
protected $lengths = array( 'firstname' => 35, 'lastname' => 30, 'default' => 100);
and call it with
Validation::checkLength($userInput, $fieldName);
with a body like
protected static function checkLength($subject, $fieldType = 'default')
{
return strlen($subject) < self::$lengthTypes($fieldType);
//or self::$fields['lengthLimit'] if you are going to structure it like Felix suggest
}
You don't need me to tell you that a function lengthChecker($subject, $limit) is pretty pointless.
EDIT - restructuring like Felix suggested isn't a bad idea depending on how far you want tot ake this. His looks more extensible. If you are going to implement the addError you probably don't want the static design.

decode mysql query before returning it to view

I'm running a query to mysql that returns encrypted data. I'd like, if possible, to decode the results before sending it to the view. It seems like better form to handle the decoding in the controller (or even the model) rather than inside the view.
I can't seem to wrap my head around how to do it, though.
I was thinking I could iterate through the object, decodode it, and push it to another array that would be sent to the view. Problem with this is I won't know (and need to keep) the indexes of the query.
So the query might return something like:
[id] => 742
[client_id] => 000105
[last] => dNXcw6mQPaGQ4rXfgIGJMq1pZ1dYAim0
[first] => dDF7VoO37qdtYoYKfp1ena5mjBXXU0K3dDlcq1ssSvCgpOx75y0A==
[middle] =>iXy6OWa48kCamViDZFv++K6okIkalC0am3OMPcBwK8sA==
[phone] => eRY3zBhAw2H8tKE
Any ideas?
Ended up with:
function name(){
$data['e_key']=$this->e_key;
$clid = $this->uri->segment(3);
$name = $this->Clients_model->getNameData('*','client_id='.$clid,'');
$nameArray= array();
foreach ($name->result() as $row){
$x = $row;
$keys = array('id','client_id');
$unenc = array();
foreach ($x as $key=>$value){
if(! in_array($key, $keys)){
$unenc[$key]=$this->encrypt->decode($value,$this->e_key);
}else{
$unenc[$key]=$value;
}
}
array_push($nameArray,$unenc);
}
$data['name'] = $nameArray;
$this->load->view('names/name_view',$data);
}
Assuming you know how to decrypt the data, it's but a matter of iterating over the object, decrypting the encrypted fields.
If $YOUR_OBJECT is your object and your function for decryption is decode() then the following code should do the trick.
// The keys corresponding to the encrypted fields
$encoded = array('last', 'first', 'middle', 'phone');
$decoded = array();
foreach($YOUR_OBJECT as $key => $value)
{
if (in_array($key, $encoded))
{
$decoded[$key] = decode($value);
}
}
if it's a particular index, you could decode it like
$result['last'] = base64_decode($result['last']);
or in the model, use mutators and accessors:
public function setUp() {
$this->setTableName('tablename');
$this->actAs('Timestampable');
$this->hasMutator('last', '_encode64');
$this->hasAccessor('last', '_decode64');
}
protected function _encode($value) {
$this->_set('last',base64_encode($value));
}
protected function _decode($value) {
return base64_decode($value); // not sure on this one - might have to
// return $this->set('last', base64_decode($value));
}

Categories