I'm struggling to find a way to convert my object to the correct format.
I want to replace a function that we currently use on generating detailed array, as you can see below everything is static.
private function departmentArray($content=[])
{
return [ static::$A_DEPT_ID => $content
, static::$O_DEPT_ID => $content
];
}
A sample result when that runs is this
{"3":{"complete":0,"incomplete":0},"5":{"complete":0,"incomplete":0}}
I converted the method
private function departmentArray($content=[])
{
$depts = d::getAllMainDepartment();
$dept_array = [];
foreach ($depts as $dept) {
$dept_array[] = array($dept->id => $content);
}
return $dept_array;
}
The resulting format looks like this
[{"3":{"complete":0,"incomplete":0}},{"5":{"complete":0,"incomplete":0}}]
How can I maintain the same format on the first version of code?
You don't push into an associative array, you use the new key as an index.
$dept_array[$dept->id] = $content;
Details
I want to
Count all my distributors that I query
Send it along within the JSON file
I know that I have 89 distributors in total, I did this dd(count($distributors));
I am sure what is the best practice for this.
Here is what I have tried
I initialize $count = 0;
Increment by 1 every time the loop execute $count = $count + 1;
Send the result toArray 'count' => $count->toArray() as part of my distributors array
Here is my code
public function index()
{
$distributors = [];
$count = 0;
foreach(
// Specific Distributors
Distributor::where('type','!=','OEM')->where('type','!=','MKP')
->get() as $distributor){
// Variables
$count = $count + 1;
$user = $distributor->user()->first();
$distributors[$distributor->id] = [
'user' => $user->toArray(),
'distributor' => $distributor->toArray(),
'hq_country' => $distributor->country()->first(),
'address' => $distributor->addresses()
->where('type','=','Hq')->first()->toArray(),
'count' => $count->toArray()
];
}
return Response::json($distributors);
}
Result
The code won't run, due to my $distributor array is not exist ...
It will run, if I take 'count' => $count->toArray() off .
Updated
I am using Laravel 4.0
The code is part of my UrlController.php
It really doesn't make a lot of sense to add this kind of count to your result. It is much simpler to just send the result and let the client do the counting. Because the information on how much distributors you actually have is right in your array of distributors. It's just it's length.
Here's an example with javascript (and $.ajax although that doesn't really matter)
$.ajax({
url: 'get/distributors',
method: 'GET'
}).done(function(data){
var count = data.length;
});
Model:
class Distributor extends Eloquent {
public function country()
{
return $this->hasOne('Country');
}
public function addresses()
{
return $this->hasMany('Address');
}
public function hqAddress()
{
return $this->addresses()->where('type', 'Hq')->first();
}
public function user()
{
return $this->hasOne('User');
}
}
Controller:
$distributors = Distributor::whereNotIn('type', ['OEM', 'MKP'])
->with('country', 'user')->get();
$count = 0;
$distributors->each(function(Distributor $distributor) use(&$count) {
$distributor->count = $count;
$count++;
});
return Response::json($distributors);
Sorry, I can be wrong.
I am not laravel expert.
But what is this fragment is about?
this Index function is part of Model?
#evoque2015 is trying to put some custom array into $distributors[$distributor->id]?
if that is the goal, could you do any test with any simple value of 'count' that sholud work in your opinion?
My guess is : 'count' index is not acceptable for your Distributor::where function
(if it is acceptable - show as the value of 'count' that doesn't break your code even if return wrong result/data ).
So I would try to change the name of this parameter either to 'my_custom_count',
should it be declared somewhere in Distributor model declaration?
Found this :
Add a custom attribute to a Laravel / Eloquent model on load?
It seems to prove my guess.
So we need to change model class like:
class Distributor extends Eloquent {
protected $table = 'distributors';//or any you have already
public function toArray()
{
$array = parent::toArray();
$array['count'] = $this->count;
return $array;
}
.....
}
and probably more changes in model or just add 'count' column to the table :-)
I've a function which returns an array. To return the value of that array I do something like this:
$obj->methodname()[keyvalue];
This works in php 5.4 only. I want to make this code work in lower php versions.
My code:
class ObjectTest {
public $ar;
function __construct() {
$this->ar = array(
1 => 'beeldscherm',
2 => 'geluidsbox',
3 => 'toetsenbord',);
}
public function arr(){
return $this->ar;
}
}
$obj = new ObjectTest();
//by calling the method and putting square brackets and the key of the element
var_dump($obj->arr()[2]);
I've rewritten the code for lower versions like this:
public function arr($arg = null){
if(is_null($arg)){
return $this->ar;
}
return $this->ar[$arg];
}
I'm doubting if this solution is an elegant one. What would you say? Any better solutions?
You can do like, store array in a variable and than access particular array index.
$arrList = var_dump($obj->arr());
echo $arrList[2];
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
how can I get a object from an array when this array is returned by a function?
class Item {
private $contents = array('id' => 1);
public function getContents() {
return $contents;
}
}
$i = new Item();
$id = $i->getContents()['id']; // This is not valid?
//I know this is possible, but I was looking for a 1 line method..
$contents = $i->getContents();
$id = $contents['id'];
You should use the 2-line version. Unless you have a compelling reason to squash your code down, there's no reason not to have this intermediate value.
However, you could try something like
$id = array_pop($i->getContents())
Keep it at two lines - if you have to access the array again, you'll have it there. Otherwise you'll be calling your function again, which will end up being uglier anyway.
I know this is an old question, but my one line soulution for this would be:
PHP >= 5.4
Your soulution should work with PHP >= 5.4
$id = $i->getContents()['id'];
PHP < 5.4:
class Item
{
private $arrContents = array('id' => 1);
public function getContents()
{
return $this->arrContents;
}
public function getContent($strKey)
{
if (false === array_key_exists($strKey, $this->arrContents)) {
return null; // maybe throw an exception?
}
return $this->arrContents[$strKey];
}
}
$objItem = new Item();
$intId = $objItem->getContent('id');
Just write a method to get the value by key.
Best regards.