I'm having a problem with an array in PHP. The thing is that want to display some users info in a table on my page, these users have several properties. I just want to display the username followed by 4 properties, this properties in my database are boolean type (true/false). In order to represent these values on the list I'm using checkboxes, so each checkbox displays checked or not checked according to the value on database. I've already got those values through a query. I'm just having issues on the displaying on my view (page).
In order to check if the property is true or false I'm using two foreach loop. I use the first one to pass for each user and the second one to evaluate the 4 properties of each user. I also preset an array that is responsible for storing if that property is true or false. So if the property is true the index "selected" on my array is going to be true.
The array structure is:
$choiceProperty['text'] // This already contains the four possible properties.
$choiceProperty['selected'] // This is going to store true if the property is true.
$choiceProperty['user_id'] // This is going to store the id of the user.
This is my code:
foreach ($user as $key => $userIndiv) {
foreach ($properties as &$choiceProperty) {
unset($choiceProperty['selected']);
if ($choiceProperty['text'] == 'A' && $userIndiv->CategoryA) {
$choiceProperty['selected'] = true;
}
if ($choiceProperty['text'] == 'B' && $userIndiv->CategoryB) {
$choiceProperty['selected'] = true;
}
if ($choiceProperty['text'] == 'C' && $userIndiv->CategoryC) {
$choiceProperty['selected'] = true;
}
if ($choiceProperty['text'] == 'D' && $userIndiv->CategoryD) {
$choiceProperty['selected'] = true;
}
$choiceProperty['user_id'] = $userIndiv->id;
}
array_push($finalChoices, $properties);
}
As you can see each $userIndiv->CategoryA, CategoryB, CategoryC... already contains the value obtained from the database.
At the end of each user I add the current $properties array to another array $finalChoices in order to check again for the next user and so on.
The problems comes when I printed the $finalChoices array to see its content and I see that it contains an array for each user with its properties but the last index of each array of each user contains the exact same value for all the arrays.
See what I mean:
First position of array:
Second position of array:
Third and last position of array:
As you can see all elements of arrays in their last position have the same values of the last position array. So they're not showing their real value. For example, based on my database the first and second arrays should have "selected" on its last position since they have 'true' in CategoryD.
I'm quite new in PHP and I'm not sure what I'm doing wrong. Of course it could be something missing on my logic that could be making me have this error. If you could help me I'd be grateful!
I also want to apologize for my English, I'm not native speaker and it's still improving. Thank you.
To finish I'd like to add a picture of how that looks on my view:
View
Related
I am trying to implement the "Edit Application Settings" feature. After a bit of thinking, my configuration values are stored in the DB with key -> value structure, like this:
id
key
value
1
logo_path
img/logo.png
As you can see, for each setting, there is only a key & value column. I made an App Service provider to cache them forever, and a helper function (config('setting_key')) to get the value, but now I'd like to update it in the most efficient way.
The user interface consists of the <form action="post" ...> and input with a corresponding name, like this: <input name="setting_key_name" ... />. As you can see, the name attribute here has the value of the key column value and the actual value of the input would be the value column value (a bit of confusion here).
First thing that came to my mind, was to make a foreach loop and find & update every row in DB, but IMHO it is very unoptimized way, cause if the page has a form with 10 values, it is 10 SQL queries. But till now, this is what I've done:
$keys = collect($request->except('_token'))->keys()->toArray();
// get all settings if the key name matches the request's input name
$setting = Setting::whereIn('key', $keys)->get();
$logo = self::GENERAL_APP_LOGO; // contant with a key-name (general_application_logo);
if($request->has(self::GENERAL_APP_LOGO) && $request->$logo) {
// Processing uploaded image here;
$this->uploadLogo($image, self::LOGO_IMAGE_PATH, $name); // Using an upload trait
$setting->where('key', $logo)->value = self::LOGO_IMAGE_PATH . $name; // just a try to update the DB this way
}
foreach ($keys as $key) {
$setting->where('key', $key)->value = $request->$key; // putting all request's input values to corresponding key
}
$setting->save(); // saving the DB.
As you can see, this won't work and will throw an Exception, like Call to undefined method ...\Eloquent\Builder::save(). I tried the same code with an update, but the difficult part here is to update it multiple times (since the if section should have the update as well, for the logo), as well as binding the key to value.
So, a little bit of your help would be appreciated - what the logic should be here? How can I update a DB rows with corresponding column's value? I mean - like this (update where key = 'general_app_name' set value, 'some_setting_value'), but using the optimized and clear way?
Working solution
As #miken32 stated in his answer, I used hid version of code, but with slight changes:
// Changed the $request->settings->keys() to PHP native method array_keys():
$settings = Settings::whereIn('key', array_keys($request->settings))->get()->groupBy('id');
// Also, here I changed the `whereIn('id', ...)` to `whereIn('key', ...)`, since it was my primary index.
foreach ($request->settings as $k=>$v) {
if ($k === self::GENERAL_APP_LOGO_ID) {
// not sure about this one, but I think this is
// how you'd access a file input in an array
$image = $request->file('settings')[$k];
$this->uploadLogo($image, self::LOGO_IMAGE_PATH, $name);
$v = self::LOGO_IMAGE_PATH . $name;
}
// take the Setting object out of the list we pulled
// Here I added the ->first() to get the first element from the retrieved collection;
$setting = $settings->get($k)->first();
$setting->value = $v;
$setting->save();
}
Since I was fetching the configuration values via helper, that only returns the value of the current key (and no id column), I changed the id to key and made the key as my PK in a model. Works like a charm!
With each setting in a separate row, there's no way to avoid multiple database queries – one to get the current values for all settings, and other to update each one. Looking up items by primary key is more efficient, so I'd recommend putting the contents of the id column in your blade view, like this:
<label for="setting_{{$setting->id}}">{{$setting->key}}</label>
<input name="settings[{{$setting->id}}]" id="setting_{{$setting->id}}" value="{{$setting->value}}"/>
Now in your controller, $request->settings will be an array you can loop through. You can continue treating your file upload separately, but now you've got the id column to look up, so change your constant to that.
$settings = Settings::whereIn('id', $request->settings->keys())->get()->groupBy('id');
foreach ($request->settings as $k=>$v) {
if ($k === self::GENERAL_APP_LOGO_ID) {
// not sure about this one, but I think this is
// how you'd access a file input in an array
$image = $request->file('settings')[$k];
$this->uploadLogo($image, self::LOGO_IMAGE_PATH, $name);
$v = self::LOGO_IMAGE_PATH . $name;
}
// take the Setting object out of the list we pulled
$setting = $settings->get($k);
$setting->value = $v;
$setting->save();
}
Note that Laravel does offer methods to bulk-update multiple models at once, but they are doing separate queries to the database in the background. IIRC, the save() method doesn't do anything if the value hasn't changed, which will spare you some hits.
You could try creating a text field, or a json field if your database supports it, and storing all of your settings as a JSON string in that field.
id
settings
1
{ "logo_path" : "img/logo.png", "foo" : "bar", "thing_count" : 17 }
2
{ "logo_path" : "img/logo2.png", "foo" : "baz", "thing_count" : 4 }
In your Laravel model, you can cast it as an array
protected $casts = ["settings" => "array"];
and then use it from the model
echo $theModel->settings['logo'];
echo $theModel->settings['foo'];
or you can cast it as a fully fledged object if you need to using value object casting.
One gotcha that can be confusing for people is the setting of the values in the array to update it. This will not work:
$theModel->settings['foo'] = "boz";
The reason is due to the way the Laravel mutators work. Instead, you make a value copy of the settings, change that, and reassign it to the model:
$settings = $theModel->settings;
$settings['foo'] = "boz";
$theModel->settings = $settings;
This approach has the capacity to infinitely expandable in the future as you just add new keys to your json. Be sure to do checks on the settings array to ensure fields you are looking for are set (which is why value objects can be very handy to do validation).
It also solves your database query problem - it's only ever one.
You don't need to put
$setting->where('key', $logo)->value = ...;
Just call
$setting->where('key', $logo)->update($request->toArray());
$setting->save(); called when you instantiated setting class like :
$setting = new Setting();
Or
$setting = Setting::whereIn('key', $keys)->get()->first();
Then
$setting->val = ...;
$setting->save(); // then it work's
I am trying to filter a view of Biblio nodes via Author ID. On the staff profiles, I have a field (field_author_id_ccsi) which contains the ID (an integer). How can I reference that via PHP in the contextual filter for views (under Advanced). Here's what I have so far but it's not working:
$node = node_load(arg(1));
if($node->field_author_id_ccsi[und][0]->value)
return $node->field_author_id_ccsi[und][0]->value;
}
else {
return;
}
You will need to open up you array object $node->field_author_id_ccsi to see where you ID is located and then drill down to it.
For example:
Let $node->field_author_id_ccsi['und'] be your array of interest.
You will need to investigate the contents of said array to see what is actually in that array. If you ID is the first element in the array then:
echo $node->field_author_id_ccsi['und'][0];
will show the integer on the screen in your view.
However a var_dump($node->field_author_id_ccsi['und']); will show you exactly where you ID is located - if it is there, as will a print_r();. You may need to use a foreach() to traverse the array if it is a multi-dimentional array.
Here's the code I used which works:
$node = node_load(arg(1));
if($node && isset($node->field_author_id_ccsi)) {
return $node->field_author_id_ccsi['und'][0]['value'];
}
return;
I'm developping a website, where if a user changes some data, it should be stored on the background, to see who did last change and what etc... . I have 1 object called Event, but the data onscreen is devided into 2 tabs (Client and Event). After the submit, I get all the fields and put the data in the object. I have this self made function to compare the values in the new boject with the values of the old object:
function createArrayReturnDiff($obj1, $obj2) {
$helpArray1 = (array) $obj1; //convert object to array
$helpArray2 = (array) $obj2; //convert object to array
$help = array_diff_assoc($helpArray2, $helpArray1); //Computes the difference of arrays with additional index check
return $help;
}
Now this works all fine, I get an array returned with names of the field and the new value.
But here comes the tricky part. After the return of this array, I loop trough it I want to check which tab the value was on in order to give beter user feedback later. So if the value is on Cleint or Event tab. Now I made 2 arrays where I describe all the fields in each tab.
$tabKlant = array('Evenementfirmanaam', 'Evenementaanspreking', 'Evenementcontactpersoon', 'Evenementcontactpersoonstraat', 'Evenementcontactpersoongemeente', 'Evenementcontactpersoonland', 'Evenementcontactpersoonmail', 'Evenementcontactpersoontel', 'Evenementgeldigheidsdatum', 'Evenementfacturatiegegevens', 'Evenementfactuur_mededeling', 'Evenementbestelbon', 'Evenementreferentie');
$tabEvenement = array('Evenementstartdatum', 'Evenementeinddatum', 'Evenementnaam', 'Evenementfeestlocatie', 'Evenementcontactfeestlocatie', 'Evenementaantal', 'Evenementact_speeches_opm', 'Evenementdj', 'Evenementinleiding');
Now my code to check:
foreach ($help as $key => $value) {
if (in_array($key, $tabEvent)) {
$tab = "Event";
} else if (in_array($key, $tabClient)) {
$tab = "Client";
} else {
$tab = "";
}
}
Now what I tried to change was Evenementfirmanaam, so the $help array contains values with key = Evenementfirmanaam and value = 'xxxx'. Everything looks like it is supposed to work. But for some reason, it can't find the value in the in_array of my foreach.
After I tried to write away data to the database. I used a mysqli_real_escape_string on the $key of my help array (firmanaam in this case) and I found out it is creating the string like: '\0Evenement\0firmanaam' . I have no idea why the \0 are added, but I have a feeling this is the reason why the in_array function won't compare my values properly. Does anyone have an idea what the problem might be?
The problem is that the firmanaam property of your Evenement class (which $obj1 and $obj2 look like to be instances of) is private, which results in the cast to array creating special keys:
If an object is converted to an array, the result is an array whose
elements are the object's properties. The keys are the member variable
names, with a few notable exceptions: integer properties are
unaccessible; private variables have the class name prepended to the
variable name; protected variables have a '*' prepended to the
variable name. These prepended values have null bytes on either side.
This can result in some unexpected behaviour.
In essence, you are being punished for violating the logical design of your class: if $firmanaam is private the outside world should not have any access to its value. The cast to array does allow you to get the value but you really should not do this.
Since you are using Evenement to encapsulate and hide data members, do it all the way. If you want access to those members, provide for and use a getter. If you want to compare two instances with specific semantics, add a comparison method to the class.
We need to extract specific fields, no matter if they have data or not.
If I f.ex. want an putput with only the field with external id : 'logo' i am trying this:
$limit = 10;
$app_id = xxxxxxx;
$items = PodioItem::filter($app_id,array('Limit' => $limit,'external_id' => 'logo'));
Within podio not all the fields are filled in, and the result is that we do not get a return value for field 'logo'.
When we don't get a return from field logo - the array output is not structured in a way so we can grab the data we need.
How do we achieve this ?
Added text
Rough Example on print output:
items[0] = Companyname,Logo,Address,Phone,Country
(has data in Logo)
items[1] = Companyname,Address,Phone,Country,ContactPerson
(no data in Logo)
items[2] = Companyname,Address,Phone,Country
PROBLEMS ARISING
items[2][4] is not existing (but i won't know this)
items[0][2] is Address Field (but i won't know this)
items[1][2] is Phone Field (but i won't know this)
Looking for Company Contact Person [x][4] accross items will end in
[0] = Wrong (country)
[1] = Right data (ContactPerson)
[2] = PHP error
Like in SQL, i would take the coloumn_name_1,coloumn_name_2, etc.. and grab data from only these fields.
PodioItem objects have a collection of item fields. As you've noted only fields that have values are included (since including empty fields is redundant). As you've noted you can't reliably access the fields by their array offset.
What you should do it access it by field_id or external_id. These are the unique identifiers. You can use the field method on the PodioItem object for this purpose:
$items = PodioItem::filter(...);
foreach ($items['items'] as $item) {
// Get field with external_id "logo". You can also pass in field_id
$field = $item->field('logo');
if ($field) {
print "Found a logo field!";
}
}
I am trying to come up with a means of working with what could potentially be very large array sets. What I am doing is working with the facebook graph api.
So when a user signs up for a service that I am building, I store their facebook id in a table in my service. The point of this is to allow a user who signs up for my service to find friends of their's who are on facebook and have also signed up through my service to find one another easier.
What I am trying to do currently is take the object that the facebook api returns for the /me/friends data and pass that to a function that I have building a query to my DB for the ID's found in the FB data which works fine. Also while this whole bit is going on I have an array of just facebook id's building up so I can use them in an in_array scenario. As my query only returns facebook id's found matching
While this data is looping through itself to create the query I also update the object to contain one more key/value pair per item on the list which is "are_friends"=> false So far to this point it all works smooth and relatively fast, and I have my query results. Which I am looping over.
So I am at a part where I want to avoid having a loop within a loop. This is where the in_array() bit comes in. Since I created the array of stored fb id's I can now loop over my results to see if there's a match, and in that event I want to take the original object that I appended 'are_friends'=>false to and change the ones in that set that match to "true" instead of false. I just can't think of a good way without looping over the original array inside the loop that is the results array.
So I am hoping someone can help me come up with a solution here without that secondary loop
The array up to this point that starts off as the original looks like
Array(
[data](
[0] => array(
are_fb_friends => false
name => user name
id => 1000
)
[1] => array(
are_fb_friends => false
name => user name
id => 2000
)
[2] => array(
are_fb_friends => false
name => user name
id => 3000
)
)
)
As per request
This is my current code logic, that I am attempting to describe above..
public function fromFB($arr = array())
{
$new_arr = array();
if((is_array($arr))&&(count($arr) > 0))
{
$this->db->select()->from(MEMB_BASIC);
$first_pass = 0;
for($i=0;$i < count($arr);$i++)
{
$arr[$i]['are_fb_friends'] = "false";
$new_arr[] = $arr[$i]['id'];
if($first_pass == 0)
{
$this->db->where('facebookID', $arr[$i]['id']);
}
else
{
$this->db->or_where('facebookID', $arr[$i]['id']);
}
$first_pass++;
}
$this->db->limit(count($arr));
$query = $this->db->get();
if($query->num_rows() > 0)
{
$result = $query->result();
foreach($result as $row)
{
if(in_array($row->facebookID, $new_arr))
{
array_keys($arr, "blue");
}
}
}
}
return $arr;
}
To search a value and get its key in an array, you can use the array_search function which returns the key of the element.
$found_key = array_search($needle, $array);
For multidimensional array search in PHP look at https://stackoverflow.com/a/8102246/648044.
If you're worried about optimization I think you have to try using a query on a database (with proper indexing).
By the way, are you using the Facebook Query Language? If not give it a try, it's useful.