drupal_write_record doesn't take object - database

In drupal 6 i used to do something like this:
<?php
/*
* CLASS Example
*/
class example {
var $id = NULL;
var $title;
var $body;
.....
// Save
function save() {
$primary_key = ($this->id == NULL ? NULL : 'id');
if (drupal_write_record('mytabble', $this, $primary_key)) {
return TRUE;
} else {
return FALSE;
}
}
}
?>
This worked quite well. But in Drupal 7, the drupal_write_record only takes an array and no longer the object $this. The new db_merge also only takes an array.
Since i want to save the properties of my object to the database, the above code was very handy and generic for all kinds of classes.
Is there an alternative way to write an object to database, or a method to place objectproperties into a an array?
Any help will be appreciated!
Robert

drupal_write_record does take an object or an array. Guess your problem is caused somewhere else.
drupal_write_record($table, &$record, $primary_keys = array())
$record: An object or array representing the record to write, passed in by reference. If inserting a new record, values not provided in $record will be populated in $record and in the database with the default values from the schema, as well as a single serial (auto-increment) field (if present). If updating an existing record, only provided values are updated in the database, and $record is not modified.
More info on drupal_write_record for D7.

Related

Storing single data values in the database

I wanted to know what the best way is to store single data values in a database. Values you only need one field of.
I have a couple of ideas.
Storing all fields in a single_data_table with a id, key and value column.
Storing it as a json object in the database.
Storing it as an array in the database.
Which one is the best way? Or might there even be better ways?
Or is it just easier to keep single data values as static data on the webpage?
At my current job we use a separate settings table for that, with two columns: key and value. And then there's a simple function to retrieve or save a setting:
function setting($key, $value = null){
$setting = DB::table('settings')->where('key', $key)->first();
if(is_null($value)){
return $setting->value ?? null;
}
if(isset($setting)){
$setting->value = $value;
}else{
$setting = $setting ?: new stdClass();
$setting->key = $key;
$setting->value = $value;
}
DB::table('settings')->insertOrReplace((array) $setting);
return true;
}
It is used like so:
$phone = setting('phone'); // Get the phone setting
$url = setting('url', 'http://example.com'); // Set url setting

Sorting a Doctrine ArrayCollection by a specific, custom field

I am attempting to sort an ArrayCollection by a specific field. The ArrayCollection is an array of courses. In the Course entity there is a method called isLive which returns a boolean.
I would like to sort this collection to have the "live" courses at the beginning of the array, so that's the courses that return true from a isLive call.
This is the code I have at present, but the first entry in the $sorted array is a non-live course.
$iterator = $this->courses->getIterator();
$iterator->uasort(function ($a, $b) {
if ($a->isLive() == $b->isLive()) {
return 0;
}
return ($a->isLive() < $b->isLive()) ? -1 : 1;
});
$sorted = new ArrayCollection(iterator_to_array($iterator));
It looks like a good use case for Doctrine Criteria. They allow to filter/sort ArrayCollections, either in memory if the collection is already loaded, either by adding a WHERE / ORDER BY SQL clause next time the collection will be loaded from the database. So that's pretty optimized!
Code should look like something like this, assuming you have a live field behind isLive():
$criteria = Criteria::create()
->orderBy(["live" => Criteria::DESC])
;
$sorted = $this->courses->matching($criteria);
For the entity, do this: add an OrderBy annotation to the property.
/**
* #OneToMany(targetEntity="Course")
* #OrderBy({"live": "ASC"})
*/
private $courses;
I got to a solution with the use of uasort and array_search as below:
/**
* #return ArrayCollection
*/
public function getCoursesSortedByLive(): ArrayCollection
{
$coursesIterator = $this->courses->getIterator();
$sortOrder = [true];
$coursesIterator->uasort(function ($a, $b) use ($sortOrder) {
return array_search($a->isLive(), $sortOrder) - array_search($b->isLive(), $sortOrder);
});
return new ArrayCollection(iterator_to_array($sitesIterator));
}

Indirect modification of overloaded element of Illuminate\Support\Collection has no effect

im quite new in laravel framework, and im from codeigniter.
I would like to add new key and value from database
static function m_get_promotion_banner(){
$query = DB::table("promotion_banner")
->select('promotion_banner_id','promotion_link','about_promotion')
->where('promotion_active','1')
->get();
if($query != null){
foreach ($query as $key => $row){
$query[$key]['promotion_image'] = URL::to('home/image/banner/'.$row['promotion_banner_id']);
}
}
return $query;
}
that code was just changed from codeigniter to laravel, since in codeigniter there are no problem in passing a new key and value in foreach statement
but when i tried it in laravel i got this following error :
Indirect modification of overloaded element of Illuminate\Support\Collection has no effect
at HandleExceptions->handleError(8, 'Indirect modification of overloaded element of Illuminate\Support\Collection has no effect', 'C:\xampp\htdocs\laravel-site\application\app\models\main\Main_home_m.php', 653, array('query' => object(Collection), 'row' => array('promotion_banner_id' => 1, 'promotion_link' => 'http://localhost/deal/home/voucher', 'about_promotion' => ''), 'key' => 0))
please guide me how to fix this
thank you (:
The result of a Laravel query will always be a Collection. To add a property to all the objects in this collection, you can use the map function.
$query = $query->map(function ($object) {
// Add the new property
$object->promotion_image = URL::to('home/image/banner/' . $object->promotion_banner_id);
// Return the new object
return $object;
});
Also, you can get and set the properties using actual object properties and not array keys. This makes the code much more readable in my opinion.
For others who needs a solution you can use jsonserialize method to modify the collection.
Such as:
$data = $data->jsonserialize();
//do your changes here now.
The problem is the get is returning a collection of stdObject
Instead of adding the new field to the result of your query, modify the model of what you are returning.
So, assuming you have a PromotionBanner.php model file in your app directory, edit it and then add these 2 blocks of code:
protected $appends = array('promotionImage');
here you just added the custom field. Now you tell the model how to fill it:
public function getPromotionImageAttribute() {
return (url('home/image/banner/'.$this->promotion_banner_id));
}
Now, you get your banners through your model:
static function m_get_promotion_banner(){
return \App\PromotionBanner::where('promotion_active','1')->get();
}
Now you can access your promotionImage propierty in your result
P.D:
In the case you are NOT using a model... Well, just create the file app\PromotionImage.php:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PromotionImage extends Model
{
protected $appends = array('imageAttribute');
protected $table = 'promotion_banner';
public function getPromotionImageAttribute() {
return (url('home/image/banner/'.$this->promotion_banner_id));
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'promotion_banner_id','promotion_link','about_promotion','promotion_active'
];
just improving, in case you need to pass data inside the query
$url = 'home/image/banner/';
$query = $query->map(function ($object) use ($url) {
// Add the new property
$object->promotion_image = URL::to( $url . $object->promotion_banner_id);
// Return the new object
return $object;
});
I've been struggling with this all evening, and I'm still not sure what my problem is.
I've used ->get() to actually execute the query, and I've tried by ->toArray() and ->jsonserialize() on the data and it didn't fix the problem.
In the end, the work-around I found was this:
$task = Tasks::where("user_id", $userId)->first()->toArray();
$task = json_decode(json_encode($task), true);
$task["foo"] = "bar";
Using json_encode and then json_decode on it again freed it up from whatever was keeping me from editing it.
That's a hacky work-around at best, but if anyone else just needs to push past this problem and get on with their work, this might solve the problem for you.

Accessing array values of a field collection in a node with Drupal?

Please bare with a very recent user of Drupal.
I want to create an array out of all examples of the string "url" on a Drupal site.
I've used the method "field_get_items" previously to do something very similar, but I am now trying to access a field collection that is many levels deep into the node's array and I'm not sure that method would work.
$website_urls = array();
$faculty_members = field_get_items('node', $node, 'field_faculty_member');
for ($i = 0; $i < count($faculty_members); $i++) {
$value = field_view_value('node', $node, 'field_faculty_member', $faculty_members[$i]);
$field_collection = $value['entity']['field_collection_item'][key($value['entity']['field_collection_item'])];
$website_urls[] = render($field_collection['field_link']['#items'][0]['url']);
}
An example of one url's location is...
['field_faculty_program'][0]['entity']['field_collection_item'][1842]['field_faculty_member'][0]['entity']['field_collection_item'][1843]['field_link']['#items'][0]['url']
..and another...
['field_faculty_program'][4]['entity']['field_collection_item'][1854]['field_faculty_member'][0]['entity']['field_collection_item'][1855]['field_link']['#items'][0]['url']
What is the method I should be using to collect al of the 'url' strings for placement in an array?
You can actually still use the field_get_items() function but eventually pass it 'field_collection_item' instead for the node type.
Something like this should work:
if ($items = field_get_items('node', $node, 'field_faculty_member')) {
//loop through to get the ids so we can take
//advantage of field_collection_item_load_multiple for
//greater efficiency
$field_collection_item_ids = array();
foreach ($items as $item) {
$field_collection_item_ids[] = $item['value'];
}
if ($field_collection_items = field_collection_item_load_multiple($field_collection_item_ids)) {
foreach ($field_collection_items as $subitem) {
//now we load the items within the field collection
if ($items = field_get_items('field_collection_item', $subitem, 'field_faculty_member')) {
//And you can then repeat to go deeper and deeper
//e.g. a field collection item within a field collection
//for instance to get the urls within your faculty members
//item. Best to break this into functions or a class
//to keep your code readable and not have so many nested
//if statements and for loops
}
}
}
}
Hope that helps!
Scott

CakePhp foreach saving only last value in array

I have a problem, right now Im using this foreach loop on CakePhp on which I want to add all the values which are still not on the table for the respecting user. To give a little more context, the user has a menu. And the admin can select which one to add for the user to use. On the next code I receive a array with the menus which will be added as so:
//This is what comes on the ['UserMenuAccessibility'] array:
Array ( [menu_accessibility_id2] => 2 [menu_accessibility_id3] => 3 [menu_accessibility_id4] => 4 [menu_accessibility_id5] => 5 [menu_accessibility_id8] => 8 )
I get the ids of the menus which want to be added to the table for the user to use. And I use the next code to add the menus to the table if they are not there still:
//I check if the array has something cause it can come with no ids.
if (!(isset($this->request->data['UserMenuAccessibility']))) {
$this->request->data['UserMenuAccessibility'] = array();
}
$UserMenuAccessibility = $this->request->data['UserMenuAccessibility'];
foreach ($UserMenuAccessibility as $key => $value) {
$conditions = array(
'UserMenuAccessibility.menu_accessibility_id' => $value,
'UserMenuAccessibility.users_id' => $id
);
if ($this->User->UserMenuAccessibility->hasAny($conditions)) {
} else {
$valuemenu['UserMenuAccessibility']['users_id'] = $id;
$valuemenu['UserMenuAccessibility']['menu_accessibility_id'] = $value;
if ($this->User->UserMenuAccessibility->save($valuemenu)) {
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
}
For some reason the array is only saving the last new id which is not on the table and not the rest. For example if I have menu 1 and 2 and add 3 and 4 only 4 gets added to the table. For some reason I cant add all the missing menu ids to the table. Any ideas why this is happening?
Thanks for the help on advance.
It looks like your code will save each item, but each call to save() is overwriting the last entry added as $this->User->UserMenuAccessibility->id is set after the first save and will be used for subsequent saves. Try calling $this->User->UserMenuAccessibility->create() before each save to ensure that the model data is reset and ready to accept new data:-
$valuemenu['UserMenuAccessibility']['users_id'] = $id;
$valuemenu['UserMenuAccessibility']['menu_accessibility_id'] = $value;
$this->User->UserMenuAccessibility->create();
if ($this->User->UserMenuAccessibility->save($valuemenu)) {
}
In cakephp 2.0 $this->Model->create() create work fine. But if you are using cakephp version 3 or greater then 3. Then follow the below code
$saveData['itemId'] = 1;
$saveData['qty'] = 2;
$saveData['type'] = '0';
$saveData['status'] = 'active';
$saveData = $this->Model->newEntity($saveData);
$this->Model->save($materialmismatch);
In normal case we use patchEntity
$this->Model->patchEntity($saveData, $this->request->data);
It will only save last values of array so you have to use newEntity() with data
In cakephp3, patchEntity() is normally used. However, when using it for inserting-new/updating entries in a foreach loop, I too saw that it only saves the last element of the array.
What worked for me was using patchEntities(), which as explained in the patchEntity() doc, is used for patching multiple entities at once.
So simplifying and going by the original code sample to handle multiple entities, it could be:
$userMenuAccessibilityObject = TableRegistry::get('UserMenuAccessibility');
foreach ($UserMenuAccessibility as $key => $value) {
$userMenuAccessibility = $userMenuAccessibilityObject->get($value);//get original individual entity if exists
$userMenuAccessibilities[] = $userMenuAccessibility;
$dataToPatch = [
'menu_accessibility_id' => $value,
'users_id' => $id
]//store corresponding entity data in array for patching after foreach
$userMenuAccessibilitiesData[] = $dataToPatch;
}
$userMenuAccessibilities = $userMenuAccessibilityObject->patchEntities($userMenuAccessibilities, $userMenuAccessibilities);
if ($userMenuAccessibilityObject->saveMany($requisitions)) {
} else {
$this->Session->setFlash(__('The users could not be saved. Please, try again.'));
}
Note: I haven't made it handle if entity doesn't exist, create a new one and resume. That can be done with a simple if condition.

Resources