I have the tables "item", "payment", "join_payment".
The item has an id, the payment has an id. The join_payment has the rows item_id and payment_id.
A payment may contain many items, which would be registered in the join_payment table.
I want to make a log with these items and I am currently doing this in the controller:
$log = Array();
$items = item::where("id",Auth::user()->id)->get();
foreach($items as $item){
$join_payment = join_payment::where("item_id",$item->id)->first();
if(!array_key_exists($join_payment->payment_id,$log){
$log[$join_payment->payment_id] = payment::where("id",$join_payment->payment_id)->first();
}
$log[$join_payment->payment_id][$item->id] = $item;
}
Is there a way to pull this out with the models?
I recommend using Eloquent relationships for this. https://laravel.com/docs/5.5/eloquent-relationships#many-to-many. If you call the join table item_payment it will be even easier:
class Item extends Model {
public function payments(){
return $this->belongsToMany(Payments::class)
}
}
class Payment extends Model {
public function items(){
return $this->belongsToMany(Item::class)
}
}
class ItemPayment extends Model {
public function item(){
return $this->belongsTo(Item::class)
}
public function payment(){
return $this->belongsTo(Payment::class)
}
}
Then you can access the data you need in a bunch of ways:
$items = Item::all();
foreach($items as $item){
$item->payments; //returns all payments belonging to this item
}
$payments = Payment::all();
foreach($payments as $payment){
$payment->items; //returns all items belonging to this payment
}
$itemPayments = ItemPayment::all();
foreach($itemPayments as $itemPayment){
$itemPayment->item; //the item for this join
$itemPayment->payment; //the payment for this join
}
Sorry for changing your class and table names, but these conventions will make your life a ton easier in Laravel
in your Item model use this
public function payment()
{
return $this->hasOne('App\Payment','join_payment','payment_id','item_id');
}
then in you loop check
foreach($items as $item){
dd($item->payment);
}
Related
I am trying to create Laravel/Vue project with two models: Category and Article. Vue part haves tree-view, which will display categories and articles tree. Categories may belong to another categories, Article may belong only to Article.
How can i form json tree from these relations?
model Category
public function articles() {
return $this->hasMany(Article::class);
}
public function childs() {
return $this->hasMany(Category::class)->union($this->files()->toBase());
}
but it shows The used SELECT statements have a different number of columns, because there is defferent fields in results.
One solution i see here is to find every article and post and create array, then jsonify it. Maybe any better solutions?
UPDATE
Done it with this code (in api controller):
public function nodes() {
$rootCategories = Category::where('category_id', null)->get();
$out = $this->_nodes($rootCategories);
return response()->json($out);
}
private function _nodes($eCategories) {
$out = [];
foreach($eCategories as $cat) {
$out[$cat->id] = $cat->toArray();
$out[$cat->id]["type"] = "folder";
$out[$cat->id]["childs"] = [];
foreach ($cat->articles as $article) {
$out[$cat->id]["childs"][$article->id] = $article->toArray();
$out[$cat->id]["childs"][$article->id]["type"] = "article";
}
if ($cat->categories) {
$out[$cat->id]["childs"] = $out[$cat->id]["childs"] + $this->_nodesCategory($cat->categories);
}
}
return $out;
}
I have an array of category , and this categories have many books (belongToMany) how i can get all book
sorry about my English
category model
class category_b extends Model
{
protected $table = 'categoryb';
protected $attributes = array(
'lang'=> 'fa',
);
public function getBooks()
{
return $this->belongsToMany('App\book','categoryb_book');
}
}
my book model
class book extends Model
{
public function getCategory()
{
return $this->belongsToMany('App\category_b','categoryb_book');
}
}
my code
$Category = $user->subscriptions(category_b::class)->pluck('id');
$bookCategory= category_b::whereIn('id',$Category)->get();
$bookCategory = $bookCategory->getBooks;
As #Makdous indicated, different approaches exist, you may take the one that fits your need.
Notice that the loop approach may lead to duplicate books, so after the loop you will have to delete duplicate records.
To avoid that problem, one solution would be to query the Book model directly:
$categoryIds = $user->subscriptions(category_b::class)->pluck('id');
$books = book::whereHas('getCategory', function ($q) use ($categoryIds) {
return $q->whereIn('categoryb.id', $categoryIds);
})
->get();
EDIT:
You may go beyond that and improve the readability of your code by defining a local scope.
In your Book class:
class book extends Model
{
// .....
// .....
// Somewhere in your Book class ...
public function scopeOfCategories($query, $categoryIds = [])
{
return $query->whereHas('getCategory', function ($q) use
($categoryIds) {
return $q->whereIn('categoryb.id', $categoryIds);
});
}
}
Now you can replace the old code snippet with the following:
$categoryIds = $user->subscriptions(category_b::class)->pluck('id');
$books = book::ofCategories($categoryIds)->get();
You can use a foreach as mentioned in the docs to iterate through the books of you're fetched category.
foreach ($bookCategory as $book) {
//access book's attributes
}
Or likewise you can get the categories of a certain book.
$book = Book::find($id);
$categories = $book->getCategory();
foreach ($categories as $category) {
//access category's attributes
}
I have the following tables:
flights(id, title, number)
stations(id, title)
flight_price(id, price, flight_id, stationA_id, stationB_id)
flight_station(flight_id, station_id)
flight_station is a pivot table.
My models:
class Flight extends Model
{
public function stations()
{
return $this->belongsToMany('App\Model\Station', 'flight_station', 'flight_id', 'station_id')
}
// To attach data
public function prices()
{
return $this->belongsToMany('App\Model\FlightPrice', 'flight_price', 'flight_id', 'stationA_id')
->withPivot('price');
}
public function price()
{
return $this->hasMany('App\Model\FlightPrice', 'flight_id');
}
}
// Station
class Station extends Model
{
public function flights()
{
return $this->belongsToMany('App\Model\Flight', 'flight_station', 'station_id', 'flight_id');
}
}
class FlightPrice extends Model
{
protected $table = 'flight_price';
public function flights()
{
return $this->belongsToMany('App\Model\Flight', 'flight_price');
}
}
I need the next result (find by id flight):
|stationA_id|stationA_title||stationB_id|stationB_title|price|
Let's say you are trying to retrieve a flight like this:
$flight = Flight::with(['price', 'stations'])->find($id);
This returns a Flight model with the Price and Station models because you eager loaded the relationships.
Now $flight->price will return the Price model associated with the Flight model. If it is not a collection - which I believe is true - the foreach loop has little meaning (or at least not the one you expect). In fact it will loop through the public attributes of the Model class (incrementing, exists, wasRecentlyCreated and timestamps).
So the following piece of code will return 4 booleans.
foreach ($flight->price as $value) {
dump($value);
}
If you want to return the station identifiers, the station titles and the price for each flight then maybe try something like this:
foreach ($flight->stations as $station) {
dump($station->id);
dump($station->title);
}
// I assume the Price model has an attribute named value
dump($flight->price->value)
A user can save ingredients to a shopping list. Now, when a User is logged in and visit a recipe, the shopping list widget is showing him, which ingredients he already have on his list, and that's my problem.
Here I'm getting all ingredients for the recipe:
$recipe = Recipe::find($id);
$recipe->load('ingredients');
Working fine with a foreach on $recipe->ingredients
And here I'm gettingthe shopping list a user has for this recipe, if he has one:
if(Auth::check()) {
$list = ShoppingList::with('ingredients')->where('recipe_id',$id)->where('user_id',Auth::user()->id)->get();
//...
}
And here I'm trying to check, if an saved ingredient is on the shopping list:
foreach($recipe->ingredients as $i) {
foreach($list as $l) {
$ingredient = Ingredients::where('id','=',$l->ingredients_id)->get();
$i->isAdded = (count($ingredient) > 0) ? 1 : 0;
}
}
But somehow that's totally wrong. What am I missing?
Relationships:
Recipe:
public function user() {
return $this->belongsTo('User','user_id');
}
public function lists() {
return $this->hasMany('ShoppingList','recipe_id');
}
public function ingredients() {
return $this->belongsToMany('Ingredients','ingredients_recipe','recipe_id','ingredients_id')->withPivot('amount');
}
Shopping List:
public function user() {
return $this->belongsTo('User','id');
}
public function recipe() {
return $this->belongsTo('Recipe','recipe_id');
}
public function ingredients() {
return $this->belongsToMany('Ingredients','shopping_list_ingredients','shopping_list_id','ingredients_id')
->withPivot(array('unit','amount'))
->withTimestamps();
}
Ingredients:
public function recipes() {
return $this->belongsToMany('Recipe','ingredients_recipe','recipe_id','ingredients_id')->withPivot('amount');
}
public function lists() {
return $this->belongsToMany('ShoppingList','shopping_list_ingredients','shopping_list_id','ingredients_id')
->withPivot(array('unit','amount'))
->withTimestamps();
}
What am I doing wrong? Thank you!
Your explanation is a bit cloudy with this code, but the point is, that you need to find missing ingredients and add them to the shopping list, is it correct?
For this you can use Collection's diff method:
// retrieve collection of the ingredients for a recipe
$recipe = Recipe::with('ingredients')->find($recipeId);
// retrieve collection of the ingredients for a shopping list
$list = ShoppingList::with('ingredients')->find($recipeId);
// find the ingredients for the recipe that are not on the list already
// return Collection of Ingredient models
$missingIngredients = $recipe->ingredients->diff($list->ingredients);
// and ingredients on both list and recipe
$missingIngredients = $recipe->ingredients->intersect($list->ingredients);
Hi i thought i can handle this myself, but actually i don't know how to bite it.
I am trying to categorise my programs. There will be only 2 levels of categories:
1 CATEGORY
2 |-Subcategory
I want it to be as simple as possible.
- program can belong to only one subcategory,
- categories can have many subcategories,
- subcategories can have many programs,
Of course i would like to list all programs from subcategories, when someone choose a main category.
I am also not sure about my current database tables structure and relationship in models.
Tables in database:
programs: id, title, description, program_subcategory_id
programs_categories: id, name
programs_subcategories: id, name, program_category_id
Models:
Program.php
class Program extends Eloquent {
protected $table = 'programs';
public function user()
{
return $this->belongsTo('User');
}
public function subcategory()
{
return $this->belongsTo('ProgramSubcategory', 'program_subcategory_id');
}
}
ProgramCategory.php
class ProgramCategory extends Eloquent {
protected $table = 'programs_categories';
public function subcategories()
{
return $this->hasMany('ProgramSubcategory');
}
}
ProgramSubcategory.php
class ProgramSubcategory extends Eloquent {
protected $table = 'programs_subcategories';
public function programs()
{
return $this->hasMany('Program');
}
public function category()
{
return $this->belongsTo('ProgramCategory');
}
}
Actual controllers:
ProgramsController.php
class ProgramsController extends BaseController {
public function index()
{
$programs = Program::with('subcategory')->orderBy('programs.id', 'desc')->paginate(5);
$acategories = ArticleCategory::All();
$pcategories = ProgramCategory::All();
return View::make('programs.index', compact('programs', 'acategories', 'pcategories'));
}
}
ProgramsSubcatecories.php
class ProgramsSubcategories extends BaseController {
public function index($cname)
{
$programs = ProgramSubcategory::whereAlias($cname)->first()->programs()->orderBy('id', 'DESC')->paginate(10);
$pcategories = ProgramCategory::All();
$scategories = ProgramSubcategory::All();
$acategories = ArticleCategory::All();
return View::make('programs.index', compact('programs', 'pcategories', 'scategories ', 'acategories'));
}
public function show($cname, $id)
{
$category = ProgramSubcategory::whereAlias($cname)->first();
$program = $category->programs()->findOrFail($id);
$pcategories = ProgramCategory::All();
$acategories = ArticleCategory::All();
return View::make('programs.show', compact('program', 'category', 'pcategories', 'scategories ', 'acategories'));
}
}
It is not a problem for me to list all items from one category with eager loading. But i have problem how to do it with 2-levels categories.
Please advise how to start it.
You are not looking for eager loading, you need to solve how to manage hierarchical data in your database.
Nested sets model serves this purpose very well. You should read some theory on Wiki: http://en.wikipedia.org/wiki/Nested_set_model
Fortunately, there are Eloquent implementations already.
To mention some:
- Baum (the best free, imho), https://github.com/etrepat/baum
- Laravel Nested Set, https://github.com/atrauzzi/laravel-nested-set
- Laravel4-nestedset, https://github.com/lazychaser/laravel4-nestedset
and the paid one (surely highest quality as well)
from Cartalyst company - https://cartalyst.com/manual/nested-sets