Laravel modifying model, after retrieving data - php

I don't want to modify the object when ever I call it, and I am looking that upon call, the modifications are done automatically.
In a controller, I have the following:
$candidate = Candidate::where('slug', '=', $slug)->first();
And then I do:
if (!is_null($candidate->social_profiles) && !empty($candidate->social_profiles)) {
$candidate->social_profiles = unserialize($candidate->social_profiles);
foreach ($candidate->social_profiles as $key => $value) {
$candidate->{$key} = $value;
}
$candidate->social_profiles = null;
}
Now, I am looking for a way, to do the last part, inside the very own Candidate model, so that whenever a candidate is retrieved, it does the code above, that way I wont have to duplicate the last part above, wherever I get a candidate.
The problem is, that I don't know where to do so. $this in __construct contains only data defined in the class, which is basically an empty model. What else?

This is probably personal taste, but I would create a CandidateRespository class that has a method findBySlug (or whatever you'd like to call it):
public function findBySlug($slug)
{
$candidate = Candidate::where('slug', '=', $slug)->first();
if (!is_null($candidate->social_profiles) && !empty($candidate->social_profiles)) {
$candidate->social_profiles = unserialize($candidate->social_profiles);
foreach ($candidate->social_profiles as $key => $value) {
$candidate->{$key} = $value;
}
$candidate->social_profiles = null;
}
return $candidate;
}
Pass the repository into your controller using dependancy injection and then call it using:
$candidate = $this->candidate->findBySlug($slug);
Here's some more reading: http://laravel.com/docs/ioc & http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories/
Hope that helps.

Related

Return conditional on method using array and/or object

So I have a single method that generated a cache key and also applies a transient automatically.
Here is the method:
private function get_cache($id, $count)
{
$cache_key = $this->generate_cache_key($id, $count);
return get_transient($cache_key);
}
How could I make that method return both the $cache_key but also get_transient?
Here is what I'm trying to achieve:
Access the $cache_key inside another method.
Also execute the get_transient when calling the method.
I have a method and this is what I'm aiming to achieve:
public function delete_cache($count = 4)
{
$cache_key = $this->get_cache['cache_key'];
var_dump($cache_key);
}
So I was thinking something like $instagram->get_cache['cache_key'] but also keep the original functionality for:
if ($cached = $instagram->get_cache($instagram->user_id, $count)) {
return $cached;
}
Does anyone know how I can get the cache_key for another method, but still keep the get_transient return?
The concept of returning multiple values from a function is called a "tuple". Almost every language implements this to some degree, sometimes as a "record", sometimes as a "database row", or maybe as a struct. For PHP, you are pretty much limited to either an object with fields, or an array, with the latter being the most common. Your get_cache function could be reworked as:
private function get_cache($id, $count)
{
$cache_key = $this->generate_cache_key($id, $count);
return [$cache_key, get_transient($cache_key)];
}
And to invoke it you'd do:
[$cache_key, $value] = $this->get_cache('a', 4);
Or, if using an older version of PHP (or you just don't like the look of that):
list($cache_key, $value) = $this->get_cache('a', 4);
The downside of this is that all callers have to be changed to support this, which may or may not be a problem. An alternative is to add an optional callback to the function that performs more work:
private function get_cache($id, $count, callable $func = null)
{
$cache_key = $this->generate_cache_key($id, $count);
$value = get_transient($cache_key);
if(is_callable($func)){
$func($cache_key, $value);
}
return $value;
}
And call it like:
$value = $this->get_cache(
'a',
4,
static function($cache_key, $value) {
var_dump($cache_key);
}
);
Although you are using WordPress, I think it is helpful to see what other frameworks do, too. PSR-6 defines something called CacheItemInterface which is the object-form of the return, and Symfony's cache (which you can actually use in WordPress, I do sometimes on large projects) uses the get-with-callback syntax.
You could return an array of those two values
private function get_cache($id, $count)
{
$cache_key = $this->generate_cache_key($id, $count);
return [$cache_key, get_transient($cache_key)];
}
// ...
[$cache_key, $transient] = get_cache($id, $count);

Why does my class only return the first value?

This Class gives me a blank output even if I change return to echo, I'm not sure what the issue is but I'm obviously not that versed in dealing with Classes and Objects.
I'm sure I'm just handling the variables/arrays incorrectly, but I can't see where, maybe the variables shouldn't be declared under Class since they should only be returned if a person is created? Should I declare variables in the function, or not declare them at all since they should be handled by $args?
Updated Question: How do I get it to return every argument not just FIRSTNAME?
PHP:
class people_handler
{
public $firstname;
public $middlename;
public $lastname;
public $city;
public $province_state;
/* zip+4 is default for postcode (postal code) */
public $postcode;
public $country;
function create_people($args)
{
$fullname=array($this->firstname,$this->middlename,$this->lastname);
$normname=array($this->firstname,$this->lastname);
$fulladdress=array($this->city,$this->province_state,$this->postcode,$this->country);
if(!$args->middlename&&$args->firstname && $args->lastname && $args->city && $args->province_state && $args->postcode && $args->country)
{
$temp_arr=array($normname,$fulladdress);
foreach($temp_arr as $value)
{
foreach($value as $values)
{
return $values;
}
}
}
else if($args->firstname && $args->middlename && $args->lastname && $args->city && $args->province_state && $args->postcode && $args->country)
{
$temp_arr=array($fullname,$fulladdress);
foreach($temp_arr as $value)
{
foreach($value as $values)
{
return $values;
}
}
}
else
{
die ("Must enter all values excluding middlename.");
}
}
}
$p1=new people_handler;
$p1->firstname="John";
$p1->middlename="Jonah";
$p1->lastname="Jameson";
$p1->city="Lansing";
$p1->province_state="Michigan";
$p1->postcode="48876-4444";
$p1->country="USA";
echo $p1->create_people($p1);
Returns:
John
You're missing the Object self-reference: $this all over the place.
Anytime you refer to a method or property from within the class, you need to refer to $this as the current instantiation of the Object that is doing the process. So, for instance...
$fullname=array($firstname,$middlename,$lastname);
becomes
$fullname=array($this->firstname,$this->middlename,$this->lastname);
Which should work, since you assigned the values to those properties already.
EDIT: Looking at the code further, constantly returning a value through loops won't manage the echoing to the browser. You can either echo $value instead of returning it, or build an array from the values and return that and have the script handle the array to echo to the browser.
EDIT THE SECOND: To get all the values out, you need to collect them as you build them. Another option is to simply output them to the browser as part of the method. Both options work, but collecting them into an array makes it more portable, but also a fair bit more code to maintain. As well, you do not need to pass the object into itself to get the method to work.
echo $p1->create_people($p1);
Should be...
$p1->create_people();
In create_people you'll have...
function create_people()
{
$fullname=array($this->firstname,$this->middlename,$this->lastname);
$normname=array($this->firstname,$this->lastname);
$fulladdress=array($this->city, $this->province_state, $this->postcode, $this->country);
if($args->firstname && $args->lastname && $args->city && $args->province_state && $args->postcode && $args->country)
{ //Don't bother including middlename if it doesn't matter if it is filled or not...
$temp_arr = array($normname, $fulladdress);
foreach($temp_arr as $value)
{
foreach($value as $values)
{
echo $values;
}
}
} else {
die ("Must enter all values excluding middlename.");
}
}
That should work.
Apart from the self-reference problem (btw the $args is also not needed as this should be the self-reference), your loop structure is wrong.
$temp_arr=array($normname,$fulladdress);
foreach($temp_arr as $value)
{
foreach($value as $values)
{
return $values;
}
}
This will:
Loop through temp_arr, finding $normname as the first value
Treat $normname as an array and loop through it
Return the first value it finds in $normname
That concludes the function, everything else is not executed.
A function can only have one return value. If you need to return information on more than one thing, you need to return it as an array or as an object so that it is all wrapped up in one element.
At the moment I'm not quite sure what you're trying to accomplish with your class, so unfortunately I can't help you with what you need to do.
Edit: You don't need to return anything in that case. Your class makes those variables accessible to all functions within the class already. With "new" you create an instance of the object, that is you create "a people_handler". This people_handler has properties about it, which you made public, so they can be set from outside the class (which may not be a great idea in a bigger project but seems fine for this). All functions which are part of the class (that is, inside it), can access what values these properties currently have for that certain people_handler by using the self-reference, $this:
class TestClass {
public fullname; //a random "property"
function echoFullname() {
echo $this->fullname; //whatever fullname is at the moment for the TestClass object we are using
}
}
$a = new TestClass(); //Create a TestClass object
$a->fullname = "Alex"; //make its name "Alex"
$b = new TestClass(); //Create another TestClass object
$b->fullname = "Carl"; //but let's name him Carl
$a->echoFullname(); //And now output the names
$b->echoFullname();
Obviously this has no practical use but hopefully illustrates how it works.As you can see, variable passing wasn't necessary at all.
at line 14:
$fullname=array($firstname,$middlename,$lastname);
Probably should be:
$fullname=array($this->firstname,$this->middlename,$this->lastname);
same one line 16:
$fulladdress=array($city,$province_state,$postcode,$country);

How specific should your functions be in Zend_Model?

if someone could help me with a conceptual question it would be great: Suppose I have a model that deals with a table called Persons. Normally I would have a standard fetch function like this:
public function fetchPersonById($person_id)
{
$result = 0;
if ((int)$person_id > 0) {
$select = $this->select()
->from($this->_name, array('Id' => 'Person_Id',
'Name' => 'Person_Name',
'Age' => 'Person_Age',
'Sex' => 'Person_Sex'));
->where('Person_Id = ?', $person_id);
$result = $this->fetchRow($select);
}
return $result;
}
Now suppose for some reason I need to fetch a person's Sex by it's Name.. and later on it's age by it's name. Would you them add different functions like:
public function fetchPersonSexByName($person_name)
{
// ...
->from($this->_name, array('Sex' => 'Person_Sex');
->where('Person_Name = ?', $person_name); ...
// ...
}
and so on... After a while you could see yourself with thousands of short methods like this.. Are you guys that specific or you wether pull the whole record (fetchall) and than later in the code just keep the column you want to use? On this case wouldn't you be breaking the whole MVC because if I want to get
someone's Sex my model (or whoeve is calling the function) would need to know the columns name in the database?
I also tought about doing something more generic like
public function $this->fetchColumnA_By_ColumnB_ColumnBValue($columnA_name,
$columnB_name, $columnA_name)
{
//...
}
And than have my short methods to be calling this more flexible column. So that I would have something like:
public function fetchPersonSexByName($person_name)
{
//...
$this->fetchColumnA_By_ColumnB_ColumnBValue('sex', 'name', 'martin');
}
Anyway.. How do you guys approach this probably common issue?
I would tend to do a version of your last generic example. The generic method would be protected (or even private) and your more specific (public) methods would call this. To avoid code repetition.
However, I'm not sure how generic I would go. May be just...
protected function _fetchColumnById($id, $column) {...}
protected function _fetchColumnByName($name, $column) {...}
...but this would depend on the requirements.
you could see yourself with thousands of short methods
If you think you'll get to 1000's of requests, then it might be better to read the whole record(s) and cache this somehow?
What you could do is make a magic __call function in your model or in his parent.
If some method doesn't exist it will go thru that magic function. Something like:
class Model_Test {
public function __call($method, $args) {
if(preg_match('/fetch([a-zA-Z]+)by([a-zA-Z]+)/i', $method, $result)) {
$fetch = $result[1];
$column = $result[2];
echo 'SELECT ' . $fetch . ' FROM test WHERE ' . $column . ' = "' . (string)$args[0] . '"';
//build your query here and make sure you make it secure with bind param, etc.
} else {
//call parent __call? Or throw an error?
}
}
}
$model = new Model_test();
$model->fetchSexByName('martin');
Just a quick example, offcourse you need to work it out. Success!

PHP: Class property chaining in variable variables

So, I have a object with structure similar to below, all of which are returned to me as stdClass objects
$person->contact->phone;
$person->contact->email;
$person->contact->address->line_1;
$person->contact->address->line_2;
$person->dob->day;
$person->dob->month;
$person->dob->year;
$album->name;
$album->image->height;
$album->image->width;
$album->artist->name;
$album->artist->id;
etc... (note these examples are not linked together).
Is it possible to use variable variables to call contact->phone as a direct property of $person?
For example:
$property = 'contact->phone';
echo $person->$property;
This will not work as is and throws a E_NOTICE so I am trying to work out an alternative method to achieve this.
Any ideas?
In response to answers relating to proxy methods:
And I would except this object is from a library and am using it to populate a new object with an array map as follows:
array(
'contactPhone' => 'contact->phone',
'contactEmail' => 'contact->email'
);
and then foreaching through the map to populate the new object. I guess I could envole the mapper instead...
If i was you I would create a simple method ->property(); that returns $this->contact->phone
Is it possible to use variable variables to call contact->phone as a direct property of $person?
It's not possible to use expressions as variable variable names.
But you can always cheat:
class xyz {
function __get($name) {
if (strpos($name, "->")) {
foreach (explode("->", $name) as $name) {
$var = isset($var) ? $var->$name : $this->$name;
}
return $var;
}
else return $this->$name;
}
}
try this code
$property = $contact->phone;
echo $person->$property;
I think this is a bad thing to to as it leads to unreadable code is is plain wrong on other levels too, but in general if you need to include variables in the object syntax you should wrap it in braces so that it gets parsed first.
For example:
$property = 'contact->phone';
echo $person->{$property};
The same applies if you need to access an object that has disalowed characters in the name which can happen with SimpleXML objects regularly.
$xml->{a-disallowed-field}
If it is legal it does not mean it is also moral. And this is the main issue with PHP, yes, you can do almost whatever you can think of, but that does not make it right. Take a look at the law of demeter:
Law of Demeter
try this if you really really want to:
json_decode(json_encode($person),true);
you will be able to parse it as an array not an object but it does your job for the getting not for the setting.
EDIT:
class Adapter {
public static function adapt($data,$type) {
$vars = get_class_vars($type);
if(class_exists($type)) {
$adaptedData = new $type();
} else {
print_R($data);
throw new Exception("Class ".$type." does not exist for data ".$data);
}
$vars = array_keys($vars);
foreach($vars as $v) {
if($v) {
if(is_object($data->$v)) {
// I store the $type inside the object
$adaptedData->$v = Adapter::adapt($data->$v,$data->$v->type);
} else {
$adaptedData->$v = $data->$v;
}
}
}
return $adaptedData;
}
}
OOP is much about shielding the object's internals from the outside world. What you try to do here is provide a way to publicize the innards of the phone through the person interface. That's not nice.
If you want a convenient way to get "all" the properties, you may want to write an explicit set of convenience functions for that, maybe wrapped in another class if you like. That way you can evolve the supported utilities without having to touch (and possibly break) the core data structures:
class conv {
static function phone( $person ) {
return $person->contact->phone;
}
}
// imagine getting a Person from db
$person = getpersonfromDB();
print conv::phone( $p );
If ever you need a more specialized function, you add it to the utilities. This is imho the nices solution: separate the convenience from the core to decrease complexity, and increase maintainability/understandability.
Another way is to 'extend' the Person class with conveniences, built around the core class' innards:
class ConvPerson extends Person {
function __construct( $person ) {
Person::__construct( $person->contact, $person->name, ... );
}
function phone() { return $this->contact->phone; }
}
// imagine getting a Person from db
$person = getpersonfromDB();
$p=new ConvPerson( $person );
print $p->phone();
You could use type casting to change the object to an array.
$person = (array) $person;
echo $person['contact']['phone'];
In most cases where you have nested internal objects, it might be a good time to re-evaluate your data structures.
In the example above, person has contact and dob. The contact also contains address. Trying to access the data from the uppermost level is not uncommon when writing complex database applications. However, you might find your the best solution to this is to consolidate data up into the person class instead of trying to essentially "mine" into the internal objects.
As much as I hate saying it, you could do an eval :
foreach ($properties as $property) {
echo eval("return \$person->$property;");
}
Besides making function getPhone(){return $this->contact->phone;} you could make a magic method that would look through internal objects for requested field. Do remember that magic methods are somewhat slow though.
class Person {
private $fields = array();
//...
public function __get($name) {
if (empty($this->fields)) {
$this->fields = get_class_vars(__CLASS__);
}
//Cycle through properties and see if one of them contains requested field:
foreach ($this->fields as $propName => $default) {
if (is_object($this->$propName) && isset($this->$propName->$name)) {
return $this->$propName->$name;
}
}
return NULL;
//Or any other error handling
}
}
I have decided to scrap this whole approach and go with a more long-winded but cleaner and most probably more efficient. I wasn't too keen on this idea in the first place, and the majority has spoken on here to make my mind up for me. Thank for you for your answers.
Edit:
If you are interested:
public function __construct($data)
{
$this->_raw = $data;
}
public function getContactPhone()
{
return $this->contact->phone;
}
public function __get($name)
{
if (isset($this->$name)) {
return $this->$name;
}
if (isset($this->_raw->$name)) {
return $this->_raw->$name;
}
return null;
}
In case you use your object in a struct-like way, you can model a 'path' to the requested node explicitly. You can then 'decorate' your objects with the same retrieval code.
An example of 'retrieval only' decoration code:
function retrieve( $obj, $path ) {
$element=$obj;
foreach( $path as $step ) {
$element=$element[$step];
}
return $element;
}
function decorate( $decos, &$object ) {
foreach( $decos as $name=>$path ) {
$object[$name]=retrieve($object,$path);
}
}
$o=array(
"id"=>array("name"=>"Ben","surname"=>"Taylor"),
"contact"=>array( "phone"=>"0101010" )
);
$decorations=array(
"phone"=>array("contact","phone"),
"name"=>array("id","name")
);
// this is where the action is
decorate( $decorations, &$o);
print $o->name;
print $o->phone;
(find it on codepad)
If you know the two function's names, could you do this? (not tested)
$a = [
'contactPhone' => 'contact->phone',
'contactEmail' => 'contact->email'
];
foreach ($a as $name => $chain) {
$std = new stdClass();
list($f1, $f2) = explode('->', $chain);
echo $std->{$f1}()->{$f2}(); // This works
}
If it's not always two functions, you could hack it more to make it work. Point is, you can call chained functions using variable variables, as long as you use the bracket format.
Simplest and cleanest way I know of.
function getValueByPath($obj,$path) {
return eval('return $obj->'.$path.';');
}
Usage
echo getValueByPath($person,'contact->email');
// Returns the value of that object path

Copy a Doctrine object with all relations

I want to copy a record with all his relations.
I'm trying with:
$o = Doctrine::getTable('Table')->Find(x);
$copy = $object->copy();
$relations = $o->getRelations();
foreach ($relations as $name => $relation) {
$copy->$relation = $object->$relation->copy();
}
$copy->save();
This code doesn't works, but I think it's on the way.
I never could get the deep copy function to operate correctly.
I manually coded a deep copy function for one of my models like this
public function copyAndSave ()
{
$filters = array('id', 'created');
$survey = $this->copy();
$survey->Survey_Entries = new Doctrine_Collection("Survey_Model_Entry");
$survey->Assignment_Assignments = new Doctrine_Collection("Assignment_Model_Assignment");
$survey->Survey_Questions = new Doctrine_Collection("Survey_Model_Question");
$survey->save();
foreach ($this->Survey_Questions as $question)
{
$answers = $question->Survey_Answers;
$newQuestion = $question->copy();
$newQuestion->survey_surveys_id = $survey->id;
$newQuestion->save();
$newAnswers = new Doctrine_Collection("Survey_Model_Answer");
foreach($answers as $answer)
{
$answer = $answer->copy();
$answer->save();
$answer->survey_questions_id = $newQuestion->id;
$newAnswers->add($answer);
}
$newQuestion->Survey_Answers = $newAnswers;
$survey->Survey_Questions->add($newQuestion);
}
return $survey->save();
}
You can read about copy() here. It takes an optional parameter $deep:
$deep
whether to duplicates the objects targeted by the relations
So
$copy = $object->copy(true);
should do it.
Sorry if I'm resurrecting this thread...
I found myself in search of a solution recently where I needed to copy a record and retain the references of the original. A deep copy $record->copy(true) copies the references, which was no good for me. This was my solution:
$record = Doctrine_Core::getTable('Foo')->find(1);
$copy = $record->copy();
foreach($record->getTable()->getRelations() as $relation) {
if ($relation instanceof Doctrine_Relation_Association) {
$ids = array();
foreach ($relation->fetchRelatedFor($record) as $r) {
$ids[] = $r->getId();
}
$copy->link($relation->getAlias(), $ids);
}
}
if ($copy->isValid()) {
$copy->save();
}
Hope this helps :)
This is how i done, but some fix is needed.
$table = $entidade->getTable();
$relations = $table->getRelations();
foreach($relations as $relation => $data) {
try {
$entity->loadReference($relation);
} catch(Exception $e) {
die($e->getMessage());
}
}
I am using Symfony1.4.1 and that uses Doctrine 1.2.1 (I think).
I have been trying to make a function that did all the above myself, when I found one that already exists.
Try this in any function and look at the results:
$tmp=$this->toArray(TRUE);
var_dump($tmp);
$this->refreshRelated();
$tmp=$this->toArray();
var_dump($tmp);
$tmp=$this->toArray(TRUE);
var_dump($tmp);
exit();
I am going to try two different things:
A/ put $this->refreshRelated() into the constructor of all my model objects.
B/ write a function that takes an array depicting the object graph that I want populated. Calling the function refereshRelatedGraph($objectGraphArray). With the right structure of the array (having all the appropriate relation names at each level), I could control which relations get populated and which don't. One use for this is to populate only children, not parent relations. The other is for when a ERD/Schema/ObjectGraph has an element that is 'owned' by more than one object (many to many, other special circumstances that I have), I could control which side of the relationships get pre(non lazy) loaded.

Categories