I have a controller that should return a list of categories. Each should also have a collection of sub-categories. Each sub-category should also have modules. The modules are of two types: the latest modules and most watched modules.
A SUMMARY STRUCTURE I WANT:
[
{
id: 1,
name: category 1,
sub_categories:
[
{
id: 1,
name: subcategory 1:
modules:
[
latestModules: [...],
mostViewedModules[...]
]
}
],
another sub-category object,
and another sub-category object
},
another category object,
and another category object,
]
My Controller
class SeriesController extends Controller
{
public function getSeries()
{
$categories = Category::with('SubCategories')->get();
$countCats = 0;
$countSubCats = 0;
collect($categories)->map(function ($category) use ($countCats, $countSubCats, $categories){
collect($category->subCategories)->map(function ($subCategory) use ($countSubCats, $categories) {
collect($categories[$countCats]->subCategories[$countSubCats])
->put('modules',
SubCategory::with('latestModules', 'mostViewedModules')
->where('id', $subCategory->id)->get()
);
$countSubCats++;
});
$countCats++;
});
return $categories;
}
}
My Category model
class Category extends Model
{
public function subCategories()
{
return $this->hasMany('App\SubCategory')
->orderby('name');
}
}
My Subcategory Model
class SubCategory extends Model
{
public function parentCategory(){
return $this->belongsTo('App\Category', 'category_id');
}
public function modules()
{
return $this->belongsToMany('App\Module', 'module_category', 'sub_category_id', 'module_id');
}
public function latestModules()
{
return $this->modules()
->orderBy('created_at', 'desc');
}
public function mostViewedModules()
{
return $this->modules()
->orderBy('views', 'desc');
}
}
My Module Model
class Module extends Model
{
public function subCategories()
{
return $this->belongsToMany('App\SubCategory', 'module_category', 'module_id', 'sub_category_id');
}
}
WHAT I WAS TRYING TO DO
1) Get all the categories via Eloquent Query
2) Collect the many categories and Map over them
3) For each ONE category i map over again to single it out into sub-categories
4) For each sub-category i run Eloquent query to return the related modules.
5) Then i put that result into categories original array by using PUT. I wanted to expand on the original $categories array by including the modules.
PROBLEM
I realize that the loop works and everything is fine, except that the global $categories variable does not update. Its like inside the loops its updated but once we exist the Map loops the value defaults to
Category::with('SubCategories')->get()
Related
Rating model
class Rating extends Model
{
protected $fillable = [
'owner_id', 'toilet_id','user_id','rating','desc',
];
public function toilet()
{
return $this->belongsTo(ToiletInfo::class);
}
}
ToiletInfo model
class ToiletInfo extends Model
{
protected $fillable = [
'owner_id', 'toilet_name','price','complex_name','address','toilet_lat','toilet_lng','status',
];
public function owner()
{
return $this->belongsTo(ToiletOwner::class);
}
public function ratings()
{
return $this->hasMany(Rating::class,'toilet_id');
}
}
RatingController
public function index()
{
return $toilets = ToiletInfo::with('ratings')->get();
//return view('admin.rating',compact('toilets'));
}
I want to get the average of rating but how to access elements inside ratings[]
Or help me improve the method I am using to get ratings for toilets that are reviewed by users
From what I understand from your question you want to get the average rating.
In your ToiletInfo model, add a new method:
public function getAverageRating()
{
$ratings = $this->ratings;
$count = $ratings->count(); // total count
$total_ratings = $ratings->sum('rating'); // add the 'rating' for all rows
return $total_ratings / $count; // average
}
In your blade file, you can simply do
$toilet->getAverageRating()
Which will give the average rating.
I have two models Member and Saving with
Member('id','name')
Saving('id','member_id','amount','month')
I have to restrict duplicate entry of saving of a member for a given month.
Member.php
class Member extends Model
{
protected $fillable=['name','address','phone'];
public function loan()
{
return $this->hasOne(Loan::class,'member_id','id');
}
public function savings()
{
return $this->hasMany(Saving::class,'member_id','id');
}
}
Saving.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Saving extends Model
{
protected $fillable=['amount','month','member_id'];
public function member()
{
return $this->belongsTo('App\Member','member_id','id');
}
}
Is it possible to implement restriction, using functions in model, or controller?
EDIT:
this is how i tried.
SavingController.php
public function addSaving(Request $request){
if($request->isMethod('get')){
$memberData = Member::all();
return view($this->_pagePath.'saving.addsaving',compact('memberData'));
//return redirect()->back();
}
if($request->isMethod('post')){
$this->validate($request,[
'amount' => 'required',
'month' => 'required'
]);
$data['amount'] = $request->amount;
$data['month'] = $request->month;
$data['member_id']= $request->name;
$member= Member::find(1);
if($member->savings()->where('month',$request->month)->exists())
{
return 'Savings for this month are already added.';
}
else
{
if(DB::table('savings')->insert($data)){
return redirect()->route('saving')->with('success','data was successfuly inserted');
}
}
}
}
An enum is a wrong column for month my friend, unless you don't care for the year. Because this way you will just be able to store savings for each month, but not for each year separately.
Your relationship is set correctly, so you can then make a check like this:
// considering you have the specific member selected
$member = Member::find('ID_OF_THE_MEMBER');
if($member->savings()->where('month', APRIL)->exists())
{
return 'Savings for this month are already added.';
}
I know this is a bit silly and probably been asked many times but for this one is unique. I'm just asking just for the sake of learning. I have these arrays inside home():
public function home()
{
$menus = [ '视频', '直播', '路亚', '海钓', '渔获' ];
$submenus1 = [ '视频', '直播', '路亚', ];
return view('/layout', [
'menus' => $menus,
'submenus1' => $submenus1,
]);
}
So it's like Nav items. And these items, I want them to be available in all views. Help would be much appreciated. Please respect. Thank you.
You can pass any data with View::share() method in App/Providers/AppServiceProvider.php file. Please check following codes;
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
$menus = [ '视频', '直播', '路亚', '海钓', '渔获' ];
$submenus1 = [ '视频', '直播', '路亚', ];
View::share('menus', $menus);
View::share('submenus1', $submenus1);
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind('path.public', function() {
return base_path('public_html');
});
}
}
After than you can use $menus and $submenus1 variables anywhere
I know Breeze is supporting enum type now and I can get enum type show in html without problem. However, I couldn't figure out how to edit this field in client.
For example:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public SexTypes SexType { get; set; }
}
public enum SexTypes
{
Male, Female
}
I want to use select tag to choose a sex type like
<select ng-model="student.sexType" ng-options="g for g in sexTypes"></select>
When I looked at metadata (via api/breeze/metadata), I do see my enum type as
enumType: {
name: "SexTypes",
isFlags: "false",
underlyingType: "Int32",
member: [
{
name: "Male",
value: "0"
},
{
name: "Female",
value: "1"
}
]
},
Now, the question is how do I populate $scope.sexTypes in my controller?
If the metadata is returned as you have explained the binding for the select would be
<select ng-model="student.sexType" ng-options="g.name for g.value in enumType.member"></select>
I don't believe Breeze supports Enum types in the way that you expect.
When Breeze reads the metadata, it sees that Student.sexType is an enum property and that the enum name is SexType. It records both facts in the data property metadata.
var property = manager.metdataStore.getEntityType('Student').getDataProperty('sexType');
console.log(property.enumType); // "Edm.Self.Color"
console.log(property.dataType.name): // "String"
But that's all that it "knows". The Breeze client MetadataStore ignores the SexType definition in the metadata from the server. It sets the DataType of the Student.sexType property to "String" because it expects the client to get and set the property with the enum's string name. And that's about it all it does
If you want to populate the dropdown with the enum values you'll have to make a list of them yourself and expose that list through the parent ViewModel (aka "controller" in ng-speak). You can either hardcode them or fish them out of the metadata stream that arrived from the server.
Breeze could ... and perhaps should ... capture the enum type and its values in the MetadataStore. I could offer an Enum Validator to detect when you set the property to other than one of the enum choices.
Unfortunately, as far as I can tell, it does neither of these things at the present time (v.1.4.5).
I think you have a worthy cause. I encourage you to express your interest in this subject on the Breeze User Voice.
As a workaround in the meantime, you could create global "enums" from your metadata like this:
manager.fetchMetadata()
.then(function (data) {
// extract all enums als global objects
ko.utils.arrayForEach(data.schema.enumType, function (c) {
window[c.name] = {};
ko.utils.arrayForEach(c.member, function (m) {
window[c.name][m.name] = m.value;
});
});
});
So if you had an enum called "SexType", you would now have a global object that you can call:
var gender = SexType.Female; //returns the value as defined in the server side enum
This is how I do it.
Step 1
Configure my server controller to return enums from url like ~/api/breeze/enums?type={0}.
Step 2
Create the dataContext provider.
.provider("dataContext", [function () {
var url = "/api/breeze";
function context() {
this.manager = new window.breeze.EntityManager(url);
}
context.prototype.query = function (entitySet) {
return new window.breeze.EntityQuery(entitySet).using(this.manager);
};
context.prototype.saveChanges = function () {
return this.manager.saveChanges();
};
this.$get = function () {
return new context();
};
}])
Step 3
Create the myEntitySet directive.
.directive("myEntitySet", ["dataContext", function (dataContext) {
return {
scope: true,
restrict: "A",
controller: ["$scope", function ($scope) {
$scope.items = [];
}],
link: function ($scope, $element, $attrs) {
dataContext.query($attrs.myEntitySet)
.execute()
.then(function (data) {
$scope.items = data.results;
$scope.$apply();
});
}
};
}])
Step 4
Use the myEntitySet directive:
<select my-entity-set="Enums?type=ProductType"
ng-model="item.Type"
ng-options="c.Name as c.Name for c in items | orderBy:'Name':true">
</select>
I have this function write in a CakePHP model:
public function getPeopleByName($name){
$this->unbindModel(array('hasMany' => array('OfficePersonTask')));
$options['fields'] = array("Person.id", "CONCAT(Person.first_name, ' ', Person.last_name) AS full_name");
return $this->find('all', $options);
}
This gave me the following json:
{
People:[
{
0:{
full_name:"Groucho Marx"
},
Person:{
id:"1"
}
},
{
0:{
full_name:"Giovanni Ferretti"
},
Person:{
id:"2"
}
}
]
}
I would that *full_name* will be part of Person group (actually is in a group called 0, all alone). How I can do that?
Use a virtual field in the model rather than a MySQL function in your find. There are some ways to query for data as you are trying to, but you have to account for data being returned in an indexed array rather than the normal associative.