Define and access virtual properties in cakePHP - where and how? - cakephp

In an application a lecture hasMany slides and a slide belongsTo a lecture. When the index function of lecture is called we would like to add a column which displays the amount off slides belonging to a certain lecture.
I tried to define this as as a $virtualFields in the lecture model like this:
public $virtualFields = array(
'slides_amount' => 'SELECT COUNT(*) AS `count` FROM `eflux`.`slides` AS `Slide` LEFT JOIN `eflux`.`lectures`
AS `Lecture` ON (`Slide`.`lecture_id` = `Lecture`.`id`)
WHERE lecture_id = \''.$id.'\'')';
The problem is that I am not able to access the current $id of the object and therefore MySQL returns an error.
My next try was to use the controller for this:
$lectures = $this->Lecture->findByCourseId($course_id);
for ($i = 0; $i < count($lectures); $i++) {
$slide_amount = $this->Lecture->Slide->find('count', array('conditions' => 'lecture_id = \' '.$id.'\''));
$slide_amounts[$i] = $slide_amount;
}
I was also not able to access the id of the current $lecture-object to determin the amount of slides belonging to that lecture.
Finally I did it in the view:
for ($i = 0; $i < count($slides_helper); $i++) {
if ($slides_helper[$i]['slides']['lecture_id'] == $lecture['Lecture']['id']) {
echo $slides_helper[$i][0]['count'];
}
}?>
Only here I could acces the id of the current object or the $lectures array. But I feel that the view is not the place to handle this.
What is best practice to achieve this goal?

in your controller, you can try:
$lectures = $this->Lecture->find('all', array('conditions'=>array('Lecture.course_id'=>$course_id)));
foreach ($lectures as &$lecture){
$lecture['Lecture']['slide_count'] = count($lecture['Slide']);
}
$this->set('lectures', $lectures);
and then access the count in your view with
foreach ($lectures as $lecture){
echo $lecture['Lecture']['slide_count'];
}
The code might not be exact... you can help us give more accurate code by pasting the results of the following code:
Place this in your controller:
$lectures = $this->Lecture->find('all', array('conditions'=>array('Lecture.course_id'=>$course_id)));
pr($lectures);
EDIT FOR PAGINATION:
Something like this might work for your pagination
$this->paginate['conditions'] = array('Lecture.course_id'=>$course_id);
$this->paginate['limit'] = '10';
$lectures = $this->paginate('Lecture');
//PERFORM THE LOOP TO ADD SLIDE COUNT
$this->set('lectures', $lectures);

Related

Eloquent - groupBy and sum() of hasManyThrough relationship

I have 3 models Job, Diary, Resource.
Jobs has relation with Diary and Diary has relation with Resource.
I wanted to get all Resource associated with a Job and did this using
public function labourers()
{
return $this->hasManyThrough(Resource::class, Diary::class, 'job_id');
}
On my Job class.
Now I want to group the results by User who's user_id is a column in Resource and then show the total hours.
This is the closest I can get.
$job = Job::where('job_number', 3007)->first();
$labour = $job->labourers()->get();
$results = $labour->groupBy('user_id');
echo $results;
foreach($results as $result)
{
$hours = $result->sum('hours');
echo $result[0]->user_id." - ";
echo $hours.". ";
}
This gets me the user_id and the sum of the hours but I am unable to access the user name through the relationship set up on the resource model
public function user()
{
return $this->belongsTo(User::class);
}
with
$result->user->name;
This produces
Property [user] does not exist on this collection instance.
How can I return a collection which allows me to access the users name and the sum of the hours.
The reason you're not able to access the user like that is because (in this case) groupBy is a method on the collection that returns another collection.
Firstly, eager load the user relationship on so that your code is a bit more efficient:
$labour = $job->labourers()->with('user')->get();
Secondly, since you have a collection you can use first() instead of [0]:
$result->first()->user_id
Lastly, you would have to access the user in the same way you're accessing the user_id:
$result->first()->user
So, you would end up with something like:
$job = Job::where('job_number', 3007)->first();
$labourers = $job->labourers()->with('user')->get();
$results = $labourers->groupBy('user_id');
foreach($results as $result)
{
echo $result->first()->user->name . ' - ' . $result->sum('hours') . '.';
}
You can try this
$job = Job::where('job_number', 3007)->with(['labourers' => function($query){
$query->select('id','user_id', DB::raw('sum(hours) as hours'))->groupBy('user_id');
}, labourers.user])->first();
$results = $job->labourers;
foreach($results as $result){
print_r($result->user);
print_r($result->hours);
}

Get or fetch Latest or last create multiple rows in laravel

I am in need of desperate help. I am newbie to laravel and programming.
I have a sales controller which fetch multiple records from form using jquery and select.
Sales Store controller looks like:
for ($i=0; $i < count($request['product_id']); ++$i)
{
$sales= new Sale;
$sales->product_id = $request['product_id'][$i];
$sales->qty= $request['qty'][$i];
$sales->user_id = Auth::user()->id;
$sales->save();
$product = new Product;
$product->where('id', '=', $request['product_id'][$i])->decrement('stock', $request['qty'][$i]);
}
this code is working perfectly fine.
Now the scenario is that I want to fetch these last created specific records to send it somehow to other page or as an invoice. Any help will be greatly appreciated on how to achieve this? Thanks.
Make a new array to hold the sales and product data and redirect to your desired url with that data.
$data = array();
for ($i=0; $i < count($request['product_id']); ++$i)
{
$sales= new Sale;
$sales->product_id = $request['product_id'][$i];
$sales->qty= $request['qty'][$i];
$sales->user_id = Auth::user()->id;
$sales->save();
$product = new Product;
$product->where('id', '=', $request['product_id'][$i])->decrement('stock', $request['qty'][$i]);
$data[]['sales'] = $sales;
$data[]['product'] = $product;
}
return redirect("/your desired url")->with('data', $data);
For your second question in the comment, In your controller function of your desired url do this -
$data = [];
if ($request->session()->has('data')) {
$data = $request->session()->get('data');
}
return view('your view', compact('data'));
And then in your view -
#foreach($data as $d)
{{$d['sales']}} //here $d['sales'] is your sales object
{{$d['product']}} //here $d['product'] is your product object
#endforeach
You can do
$sales = Sale::latest()->take(count($request['product_id']))->get();
latest() will just order all the sales by the date of creation in descending order.
if count($request['product_id']) is 5, take() will fetch the first 5 sales.

How to fetch records with order by Rand and an another field in order cake php 3?

I am fetching video ids from database with random order in limit of 30 and but want to show videos with date of publish in desc in cake php 3. Please assist
In fact, your question is very vague and you don't provide any code, that's why you are down-voted. Next time, try to show you tried something, because even if you did, this is a two-lines-long question.
I understood that :
You want to get 30 random videos from a certain database;
You want to sort them by publish date (desc).
So, try :
// Retrieve all ids
$ids = $this->Videos->find('list')->toArray();
$ids = array_keys($ids);
// Select 30 random ids from the ids list
$total = count($ids);
$count = 30 <= $total ? 30 : $total;
$selectedIds = [];
for($i = 0; $i < $count; $i++) {
$newId = -1;
do {
$newId = rand(0, $total - 1);
} while(!in_array($newId, selectedIds));
$selectedIds[] = $newId;
}
// Now you got your ids in an array
$videos = $this->Videos->find('all')
->where(['id' => $selectedIds])
->order(['publish_date' => 'DESC'])
->limit(30);

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