I can't filter a collection with a variable:
$sections = Section::all();
$courses = array("MATH282", "MATH201" , "GEOM202");
foreach ($courses as $course)
{
$sections1 = $sections->filter(function($foo)
{
if ($foo->course == $course ) {
return true;
}
});
}
The filter works if the condition is a string. Everything else works.
You are missing use() function in filter closure function. Your code should be as:
$sections = Section::all(); $courses = array("MATH282", "MATH201" , "GEOM202");
foreach ($courses as $course) {
$sections1 = $sections->filter(function($foo) use($course) {
if ($foo->course == $course ) {
return true;
}
});
}
Related
I am doing the function of creating dynamic menu in laravel and I have the tbl_menu table as below:
menu_id parent_id menu_name link order
1 0 Level_1 url_1 1
2 0 Level_1 url_2 1
3 2 Level_2 url_3 2
4 2 Level_2 url_4 3
5 4 Level_3 url_5 3
I want the result to be an array that looks like this:
-Level_1
-Level_2
-Level_2
-Level_1
_Level_2
_Level_2
_Level_3
My code is as follows:
public function SelectAllChildeLv1ByParentId($id){
$result = DB::table('tbl_menu')->where('parent_id', $id)
->orderBy('order', 'asc')->get();
return $result;
}
public function SelectAllMenuParent(){
$result = DB::table('tbl_menu')->where('parent_id', 0)
->orderBy('order', 'asc')->get();
return $result;
}
public function getMenuChildren($listParent, $step = 3)
{
$results = [];
if ($step > 0) {
$step--;
if($listParent) {
foreach($listParent as $index => $parent) {
$listChild = $this->SelectAllChildeLv1ByParentId($parent->menu_id);
$listChild1 = $this->getMenuChildren($listChild, $step);
$results[$index] = $parent;
$results[$index]->menu_child = $listChild1;
}
}
}
return $results;
}
public function getAllMenus() {
$allMenuParent = $this->SelectAllMenuParent();
$results = $this->getMenuChildren($allMenuParent);
return $results;
}
The results are correct but are running too many queries. I want to optimize it but haven't figured it out yet.
Thanks for any help.
try :
public function get()
{
$menu = DB::table('tbl_menu')
->orderBy('order', 'asc')->get();
$parents = $menu->where('parent', 0);
$items = self::tree($parents, $menu);
return $items;
}
public function tree($items, $all_items)
{
$data_arr = array();
foreach ($items as $i => $item) {
$data_arr[$i] = $item->toArray(); //all column attributes
$find = $all_items->where('parent', $item->id);
//$data_arr[$i]['tree'] = array(); empty array or remove if you dont need it
if ($find->count()) {
$data_arr[$i]['tree'] = self::tree($find, $all_items);
}
}
return $data_arr;
}
Use eloquent.
First, add this to the 'tbl_menu' model:
protected $with = ['children'];
public function children()
{
return $this->hasMany(YOUR_CLASS_NAME::class, 'parent_id', 'id')->orderBy('order');
}
Now if you call the main menu, and all children.
etc: YOUR_CLASS_NAME::where('parent_id', 0)->get().
Then you can use recursion when calling
public $menus = [];
public $level = 0;
public function getMenus($request){
$menus = YOUR_CLASS_NAME::where('parent_id', 0)
->orderBy('order')
->get();
$this->recursiveMenu($menus);
return $this->menus;
}
public function recursiveMenu($menus, $lvl = 0){
foreach ($menus as $key => $menu) {
$this->menus[$menu->id] = str_repeat('—', $lvl).' '.$menu->title;
if($menu->children){
$this->level++;
$this->recursiveMenu($menu->children, $this->level);
}
if (!isset($menu[$key+1])) {
$this->level--;
}
}
}
I got two functions in my model:
function getPersonsByGroup($groupId, $callback) {
$group = StutGroup::where('stg_id', $groupId)->get();
$persons = [];
foreach($group as $gr) {
foreach($gr->students as $stud) {
$persons[] = $stud->person;
}
}
return $callback(collect($persons));
}
function joinStudentsToPersons($person) {
return $person->each(function ($pers) {
$pers->student = \DB::connection('pgsql2')->table('students')->where('stud_pers_id', $pers->pers_id)->get();
});
}
I'm trying to call the function getPersonsByGroup in my controller passing the reference to the callback as follows:
$students = $studGroup->getPersonsByGroup($request->group, $studGroup->joinStudentsToPersons);
But if I pass an anonymous function to the getPersonsByGroup everything works well:
$students = $studGroup->getPersonsByGroup($request->group, function($person) {
return $person->each(function ($pers) {
$pers->student = \DB::connection('pgsql2')->table('students')->where('stud_pers_id', $pers->pers_id)->get();
});
});
What am I doing wrong?
The solution to your problem, if you want to keep this kind of structure, is to make the method return the closure like so:
function joinStudentsToPersons() {
return function ($person) {
$person->each(function ($pers) {
$pers->student = \DB::connection('pgsql2')->table('students')
->where('stud_pers_id', $pers->pers_id)
->get();
});
};
}
And then call it like:
$students = $studGroup->getPersonsByGroup($request->group, $studGroup->joinStudentsToPersons());
I am creating an API and I want to add extra column in the result of pagination query. For example I have price and discount in my database. I want to send discounted_price column with result set.
Here is what I have tried so far:
Controller:
$products = Products::latest()->paginate(10);
if (! empty($products)) {
$final_prod = [];
foreach ($products as $product) {
$final_prod[] = $product->asFilterJson();
}
$data['products'] = $final_prod;
$data['status'] = 200;
} else {
$data['error'] = "No product available";
}
and in my Products model I have
public function asFilterJson() {
$json = [];
$json['id'] = $this->id;
$json['title'] = $this->title;
$json['category_id'] = $this->category_id;
$json['price'] = $this->price;
$json['description'] = $this->description;
$json['quantity'] = $this->quantity;
$json['discount'] = $this->discount;
$json['type_id'] = $this->type_id;
$json['created_by_id'] = $this->created_by_id;
$json['created_at'] = $this->created_at;
$json['updated_at'] = $this->updated_at;
if($this->type_id == self::ITEM_SPECIAL) {
$json['discounted_price'] = ($this->discount * $this->price) / 100; }
return $json;
}
It works fine but it removes the pagination.
you can add key and value in collection object by using map method
$products = Products::latest()->paginate(10);
$itemSpecial = self::ITEM_SPECIAL; //pass variable in closure by using use
$products->map(function($item) use ($itemSpecial) {
if($item->type_id == $itemSpecial) {
$item->discounted_price = ($item->discount * $item->price) / 100;
}
return $item;
});
you can used condition also in clourse
In Controller
public function index() {
$products = Products::latest()->paginate(10);
if (! empty($products)) {
$final_prod = [];
foreach ($products as $product) {
$final_prod[] = $this->asFilterJson($product);
}
return response()->json(['status'=>'200','message'=>'Product list ','data'=>$final_prod]);
} else {
return response()->json(['status'=>'200','message'=>'No product available','data'=>[]]);
}
}
// function for extra column add
static function asFilterJson($product){
$value['discounted_price'] = ($value['discount'] * $value['price']) / 100;
return $value;
}
You can define new mutator in your model.
public function getDiscountedPriceAttribute()
{
return ($this->discount * $this->price) / 100;
}
After than, you can use it as $this->discountedPrice
I would like to move a helper to be displayed in all views. The helper is ->
helperFunctions::getPageInfo($cart, $total);
At this moment I have to define in every controller that information, as an example:
public function show($id, Request $request)
{
$category = Category::find($id);
if (strtoupper($request->sort) == 'NEWEST') {
$products = $category->products()->orderBy('created_at', 'desc')->paginate(40);
} elseif (strtoupper($request->sort) == 'HIGHEST') {
$products = $category->products()->orderBy('price', 'desc')->paginate(40);
} elseif (strtoupper($request->sort) == 'LOWEST') {
$products = $category->products()->orderBy('price', 'asc')->paginate(40);
} else {
$products = $category->products()->paginate(40);
}
helperFunctions::getPageInfo($sections, $cart, $total);
return view('site.category', compact('cart', 'total', 'category', 'products'));
}
I read and tried to move that helper to AppServiceProvider.php, inside the Boot() function.
public function boot()
{
helperFunctions::getPageInfo($cart,$total);
View::share('cart','total');
}
But im receiving error info:
Undefined variable: total
-----UPDATE--------
My class helper with getPageInfo
class helperFunctions
{
public static function getPageInfo(&$cart,&$total)
{
if (Auth::user()) {
$cart = Auth::user()->cart;
} else {
$cart = new Collection;
if (Session::has('cart')) {
foreach (Session::get('cart') as $item) {
$elem = new Cart;
$elem->product_id = $item['product_id'];
$elem->amount = $item['qty'];
if (isset($item['options'])) {
$elem->options = $item['options'];
}
$cart->add($elem);
}
}
}
$total = 0;
foreach ($cart as $item) {
$total += $item->product->price*$item->amount;
}
}
}
Since you pass the variable by reference in your helper function, you need to define them before calling. Change your view share code to this.
public function boot()
{
$cart = null;
$total = 0;
helperFunctions::getPageInfo($cart, $total);
$data = [
'cart' => $cart,
'total' => $total,
];
View::share($data);
}
You can then access $cart and $total in all your views.
Alternatively if you want to make things cleaner, make the helper function return the array of data and pass it to the view.
I have a dropDownList in my view, it is populating from clients table, the table contains columns like first_name, last_name,id etc., Now I want to show the first_name and last_name as display text and id as value in drop down list, I'm done with id as value and first_name as display text, but here I want to combine those columns (first_name and last_name) and use as display text.
in model
function getClients()
{
$Clients = Client::model()->findAll();
$list = CHtml::listData($Clients , 'client_id', 'first_name');
return $list;
}
in view
echo $form->dropDownList($model,'client_id',$model->getClients());
Heres another method
In model
function getFullName()
{
return $this->first_name.' '.$this->last_name;
}
and
function getClients()
{
$Clients = Client::model()->findAll();
$list = CHtml::listData($Clients , 'client_id', 'fullName');
return $list;
}
I think this is kinda reusable method since you can use the fullName virtual attribute not only in dropdownlist but everywhere a full name is needed.
1st variant (Easy):
$list = array();
foreach ($Clients as $c) {
$list[$c->id] = $c->first_name . ' ' . $c->last_name;
}
2nd variant (Universal):
class ExtHtml extends CHtml {
public static function listData($models,$valueField,$textField,$groupField='')
{
$listData=array();
if($groupField==='')
{
foreach($models as $model)
{
$value=self::value($model,$valueField);
if (is_array($textField)) {
$t = array();
foreach ($textField as $field) {
$t[]=self::value($model,$field,$field);
}
$text=implode(' ', $t);
} else {
$text=self::value($model,$textField, null);
if ($text == null) {
if (is_callable($textField)) $text=call_user_func($textField, $model);
else $text = $textField;
}
}
$listData[$value]=$text;
}
}
else
{
foreach($models as $model)
{
$group=self::value($model,$groupField);
$value=self::value($model,$valueField);
if (is_array($textField)) {
$t = array();
foreach ($textField as $field) {
$t[]=self::value($model,$field,$field);
}
$text=implode(' ', $t);
} else {
$text=self::value($model,$textField, null);
if ($text == null) {
if (is_callable($textField)) $text=call_user_func($textField, $model);
else $text = $textField;
}
}
$listData[$group][$value]=$text;
}
}
return $listData;
}
public static function value($model,$attribute,$defaultValue=null)
{
foreach(explode('.',$attribute) as $name)
{
if(is_object($model) && ($model->hasAttribute($name) || isset($model->{$name})))
$model=$model->$name;
else if(is_array($model) && isset($model[$name]))
$model=$model[$name];
else
return $defaultValue;
}
return $model;
}
}
// in model
function getClients()
{
$Clients = Client::model()->findAll();
$list = ExtHtml::listData($Clients , 'client_id', array('first_name', 'last_name'));
return $list;
}
3rd variant (Mysql)
function getClients()
{
$Clients = Client::model()->findAll(array('select' => 'concat(first_name, " ", last_name) as first_name'));
$list = CHtml::listData($Clients , 'client_id', 'first_name');
return $list;
}
Try this compact mode using "anonymous function" feature of PHP:
function getClients()
{
return CHtml::listData(Client::model()->findAll(), 'id', function($client) { return $client->first_name . ' ' . $client->last_name; });
}
Previous comment with "compact mode" with anonymous function has an error!
Right code is:
function getClients()
{
$clients = Client::model()->findAll();
return CHtml::listData($clients, 'id', function($clients) { return $client->first_name . ' ' . $client->last_name; });
}