Laravels Eloquent to store many to many relationship - laravel-4

I have 3 tables
-- networks
Schema::create('networks', function(Blueprint $table)
{
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('network');
$table->string('description',255);
$table->string('attributes',255);
$table->timestamps();
});
and
-- campaigns
Schema::create('campaigns', function($table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('campaign_title');
$table->integer('owner')->unsigned();
$table->string('details');
$table->timestamps();
});
Schema::table('campaigns', function($table) {
$table->foreign('owner')->references('id')->on('users')->onDelete('cascade');
});
and
-- campaign_networks_relationhsips
Schema::table('campaign_networks_relationhsips', function($table)
{
$table->foreign('campaign_id')->references('id')->on('campaigns')->onDelete('cascade');
$table->foreign('network_id')->references('id')->on('networks')->onDelete('cascade');
});
On new Campaign creation, in the Form I show available Networks as checkboxes.
User gives a title to the campaign and ads the desired Networks to it and saves.
The question:
has Eloquent a method which would pull this relation directly to the binding tables(campaign_networks_relationhsips) or I have to make 2 queries like saving the campaign and after getting the id of the campaign I use a loop on networks to save this relations in my binding table.
example
created campaign: gives me back id:1
choosed networks 3,4
than loop and save
1-3
1-4
in campaign_networks_relationhsips
than I try the following
<?php namespace Td\Reports\Controllers\Backend;
use Td\Reports\Campaigns\CampaignsInterface;
use Input,
Redirect,
View,
App,
Str;
use Illuminate\Support\MessageBag;
class CampaignsController extends ObjectBaseAdminController {
/**
* The place to find the views / URL keys for this controller
* #var string
*/
protected $view_key = 'admin.campaigns';
protected $networks;
/**
* Construct
*/
public function __construct(CampaignsInterface $campaigns) {
$this->model = $campaigns;
$networks = App::make('Td\Reports\Networks\NetworksInterface');
$this->networks = $networks->getAll();
parent::__construct();
}
public function postNew() {
$record = $this->model->getNew(Input::all());
//$record->campaign_title= Input::get('campaign_title');
$valid = $this->validateWithInput === true ? $record->isValid(Input::all()) : $record->isValid();
if (!$valid)
return Redirect::to('admin/' . $this->new_url)->with('errors', $record->getErrors())->withInput();
// Run the hydration method that populates anything else that is required / runs any other
// model interactions and save it.
$record->save();
$record->networks()->sync([3,4]);
return Redirect::to($this->object_url)->with('success', new MessageBag(array('Item Created')));
}
}
than I have the repository for campaigns
<?php
namespace Td\Reports\Campaigns;
use Td\Reports\Core\EloquentBaseRepository;
use Td\Reports\Abstracts\Traits\NetworkableRepository;
use Datatables,Sentry;
class CampaignsRepository extends EloquentBaseRepository implements CampaignsInterface {
/**
* Construct
* #param Campaigns $campaigns
*/
public function __construct(Campaigns $campaigns) {
$this->model = $campaigns;
public function getAll() {
if (Sentry::getUser()->hasAnyAccess(['system'])) {
return $this->model->get();
} else {
return $this->model->where('owner', Sentry::getUser()->id)->get();
}
}
public function networks()
{
return $this->belongsToMany('Network', 'campaign_networks_relationhsips');
}
}

A must read for you: http://laravel.com/docs/eloquent#relationships
From what you wrote I assume you want to save a model and its pivot relations, so here it goes:
// basic example flow in a controller, repo or wherever you like
$campaign = new Campaign;
$campaign->name = Input::get('name');
// assign more campaign attributes
$campaign->save();
$campaign->networks()->sync([3,4]); // 3,4 are existing network rows ids
That's all. For the above to work you need to setup these relationships:
// Campaign model
public function networks()
{
return $this->belongsToMany('Network', 'campaign_networks_relationhsips');
}
// Network model
public function campaings()
{
return $this->belongsToMany('Campaign', 'campaign_networks_relationhsips');
}

Related

How can i store more than one product_ticket?

Im trying to store more than one product with my controller. In my function store first i generate a ticket and then i generate a product_ticket with the ticket_id recently generated and the product_id from the selected product from the form. But how can i do to store more than one if i select more than one product in the form.
This are my relationships:
Product.php:
class Product extends Model
{
public function tickets()
{
return $this->belongsToMany(Ticket::class);
}
public function productXticket()
{
return $this->hasMany(ProductXTicket::class);
}
}
Ticket.php
class Ticket extends Model
{
public function productXticket()
{
return $this->hasMany(ProductXTicket::class);
}
public function products()
{
return $this->belongsToMany(Product::class);
}
}
ProductXTicket:
class ProductXTicket extends Model
{
protected $table = 'product_ticket';
public function ticket_id(){
return $this->belongsTo(Ticket::class);
}
public function product_id(){
return $this->belongsTo(Product::class);
}
}
product_ticket migration:
Schema::create('product_ticket', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id')->constrained();
$table->foreignId('ticket_id')->constrained();
$table->string('serial_number');
$table->integer('quantity');
$table->timestamps();
});
}
TicketController:
public function store(Request $request){
/*dd($request->all());*/
$ticket = new Ticket();
/*$ticket->cuenta_id = $request->cuenta_id;*/
$ticket->contact_id = $request->contact_id;
$ticket->statusTicket_id = $request->statusTicket_id;
$ticket->typeTicket_id = $request->typeTicket_id;
$ticket->idOwner = Auth::user()->id;
$ticket->save();
$productXticket = new ProductXTicket();
$productXticket->ticket_id = $ticket->id;
$productXticket->serial_number = $request->serial_number;
$productXticket->quantity = $request->quantity;
$ticket->productXticket()->save($productXticket);
Session::flash('success');
return redirect()->route('tickets.view');
}
So here im storing one ticket and one product_ticket. I want form each ticket to store the same amount of product_ticket as the amount products selected
Instead of:
$ticket->productXticket()->save($productXticket);
Use:
$ticket->productXticket()->saveMany([$productXticket,...]);

Can't access pivot model's id attribute inside of the static::saved() method in Laravel

I can't access pivot model's id attribute. I have one pivot model PivotModel and two models that are connected through this pivot model
ModelA class:
public function modelB()
{
return $this->belongsToMany(ModelB::class, 'model_a_model_b', 'model_a_id', 'model_b_id')
->using(PivotModel::class)
->withPivot('id', 'prop_1', 'prop_2');
}
ModelB class:
public function modelA()
{
return $this->belongsToMany(ModelA::class, 'model_a_model_b', 'model_b_id', 'model_a_id')
->using(PivotModel::class)
->withPivot('id', 'prop_1', 'prop_2');
}
PivotModel:
use Illuminate\Database\Eloquent\Relations\Pivot;
class PivotModel extends Pivot
{
public $incrementing = true;
public static function boot() {
parent::boot();
static::saved(function ($model) {
dump($model->id);
dump($model->toArray());
});
}
}
Pivot table migration file
Schema::create('model_a_model_b', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('model_a_id');
$table->unsignedInteger('model_b_id');
$table->string('prop_1');
$table->string('prop_2');
$table->unique(['model_a_id', 'model_b_id'], 'model_a_id_model_b_id');
$table->foreign('model_a_id')
->references('id')->on('model_a')
->onDelete('cascade')
;
$table->foreign('model_b_id')
->references('id')->on('model_b')
->onDelete('cascade')
;
$table->timestamps();
});
I assume this should work.
This is from the official documentation for Laravel 5.8
Custom Pivot Models And Incrementing IDs
If you have defined a many-to-many relationship that uses a custom pivot model, and that pivot model has an auto-incrementing primary key, you should ensure your custom pivot model class defines an incrementing property that is set to true.
/**
* Indicates if the IDs are auto-incrementing.
*
* #var bool
*/
public $incrementing = true;
I can only access the prop_1 and prop_2 properties but not the id property.
The id is null
dump($model->id);
and the toArray() only shows other props but not the id
dump($model->toArray());
I found a temporary solution. If you know how to do it better please suggest.
As mentionied, the id property is accessible in the created() method
so you can easily get it using $model->id.
static::created(function ($model) {
dump($model->id);
});
The problem is in the updated() method where the $model instance is filled with properties other than id.
See the method updateExistingPivotUsingCustomClass inside of Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithPivotTable
static::updated(function($model) {
// If the model is of type Pivot we have to store it into a different variable or overwrite it. If we need dirty props we first need to store them to separate variable before overwriting the $model variable
$dirtyProps = $model->getDirty();
if($model instanceof Pivot) {
$model = get_class($model)
::where($model->foreignKey, $model->{$model->foreignKey})
->where($model->relatedKey, $model->{$model->relatedKey})
->firstOrFail();
// Use the model
$model->id ...
}
});

How do I add a new record ith belongsToMany and hasMany relationships using a pivot table?

I just setup model Asset with belongsToMany rooms and model Room with hasMany assets relationships. I also created a pivot table that stores both the ids of room and assets.
MODELS
class Asset extends Model
{
protected $fillable = [ 'name', 'slug', 'price' ];
public function room()
{
return $this->belongsToMany('App\Room');
}
}
class Room extends Model {
protected $fillable = [ 'number', 'status' ];
public function assets()
{
$this->hasMany('App\Asset'); }
}
}
MIGRATIONS
public function up()
{
Schema::create('assets', function (Blueprint $table)
{
$table->increments('id');
$table->string('name')->unique();
$table->string('slug');
$table->string('price');
$table->timestamps();
$table->softDeletes();
});
}
public function up()
{
Schema::create('asset_room', function(Blueprint $table) {
$table->integer('asset_id')->unsigned();
$table->foreign('asset_id')->references('id')->on('assets');
$table->integer('room_id')->unsigned();
$table->foreign('room_id')->references('id')->on('rooms');
$table->unique(['asset_id', 'room_id']);
$table->timestamps();
});
}
I added an asset via php artisan tinker as:
$asset = new App\Asset;
$asset->name = "Lamp";
$asset->slug = "lamp";
$asset->price = 40;
$asset->save();
Now how do I add an asset to a room so that it also adds an entry to the pivot table?
$room = new App\Room;
$room->number = 1;
$room->status = 1;
$room->asset...?
$room->save();
UPDATE
On Alexey's suggestion, I've updated the following function in Room modal since it's a belongsToMany and not a hasMany relationship.
public function assets()
{
$this->belongsToMany('App\Asset');
}
I created an asset with id 1. When trying to attach asset to room as:
$room->assets()->attach(1);
I get the following error:
PHP Fatal error: Call to a member function attach() on null in C:\xampp\htdocs\hotel\vendor\psy\psysh\src\Psy\ExecutionLoop‌​\Loop.php(90) : eval()'d code on line 1
which also breaks the Tinker mode (Psy Shell) execution.
First of all, since it's a many-to-many relationship, it should be belongsToMany():
public function assets()
{
return $this->belongsToMany('App\Asset');
}
To attach an asset to a room, use attach() method:
$asset = new App\Asset;
$asset->name = "Lamp";
$asset->slug = "lamp";
$asset->price = 40;
$asset->save();
$room = new App\Room;
$room->number = 1;
$room->status = 1;
$room->save();
$room->assets()->attach($asset->id);

Get posts from multiple users in Laravel

In my site, I have a table of Users. Users can follow each other. A user can created any number of Post's.
I want to be able to see the most recent Post's from the users I've followed.
Currently my models are defined like this:
User Model:
class User extends Authenticatable
{
public function followers(){
return $this->hasMany('App\Follower', 'following');
}
public function following(){
return $this->hasMany('App\Follower', 'id');
}
public function posts(){
return $this->hasMany('App\Post', 'createdby');
}
}
Follower Model:
class Follower extends Model
{
public function postsFollowing(){
return $this->hasMany('App\Post', 'createdby', 'following');
}
}
Post Model:
class Post extends Model
{
public function user(){
return $this->belongsTo('App\User', 'createdby', 'id');
}
}
My tables are as such:
Table Name, column names
User id, name
Follower id, following
Post id, created_by
In the Follower table, id represents the user, and following represents the user being followed. If user 3 follows user 537, then id = 3, following = 537. Hope that made sense.
What I've tried:
User::following()->posts - doesn't work because
User::following() returns an Eloquent Collection object. You have
to loop through this
Looping through my followed users to get their Post's - This
doesn't work either since I want to get the top n entries sorted by
date.
Update #1
Follower Model (Updated)
class Follower extends Model
{
public function followingPosts(){
return $this->hasManyThrough(
'App\Post', 'App\Follower',
'following', 'createdby', 'id'
);
}
}
Controller
$user = Auth::user();
$posts = $user->followingPosts;
I updated followingPosts() in the Follower class with the above. The result: $posts is null
Update #2
I moved the followingPosts() to the User model:
public function followingPosts(){
return $this->hasManyThrough(
'App\Post', 'App\Follower',
'following', 'createdby'
);
}
Controller:
$user = Auth::user();
$posts = $user->followingPosts;
Now I just get all posts, even from the users I didn't follow.
Your requirement - "Users can follow each other. A user can created any number of Post's. Being able to list recent posts (limited to number) of followers or whom user is following".
You can define many-to-many relationship on the User Model (Many-To-Many relationship on self - User Model).
Create two Pivot tables
Follower-User Pivot Table
class CreateFollowerUserPivotTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('follower_user', function (Blueprint $table) {
$table->integer('follower_id')->unsigned();
$table->foreign('follower_id')->references('id')->on('users')->onDelete('cascade');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
$table->primary(['follower_id', 'user_id']);
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('follower_user');
}
}
Following-User Pivot Table
class CreateFollowingUserPivotTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('following_user', function (Blueprint $table) {
$table->integer('following_id')->unsigned();
$table->foreign('following_id')->references('id')->on('users')->onDelete('cascade');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
$table->primary(['following_id', 'user_id']);
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('following_user');
}
}
Then define the relationships in your User Model
class User extends Model
{
public function followers()
{
return $this->belongsToMany(User::class, 'follower_user', 'user_id', 'follower_id')->withTimestamps();
}
public function following()
{
return $this->belongsToMany(User::class, 'following_user', 'following_id', 'user_id' )->withTimestamps();
}
//Assuming posts table has user_id as foreign key
public function posts()
{
return $this->hasMany(Post::class);
}
//Assuming posts table has user_id as foreign key
public function recent_posts()
{
return $this->hasMany(Post::class)->take(10)->orderBy('created_at', 'desc');
}
}
Now to get who a given user is following
//Say for example we take the logged in user
$user = User::with('following.recent_posts')->whereEmail(auth()->user()->email);
foreach($user->following as $following)
{
$posts = $following->recent_posts;
}
Hope this is what you are trying to accomplish.
You can use has-many-through for this as:
public function followingPosts()
{
return $this->hasManyThrough(
'App\Post', 'App\Follower',
'follow', 'createdby', 'id'
);
}
Then you can access the posts as:
$user->followingPosts; // returns collection of post model
Note: Assuming you have a follow column in Follower table.

Display values of a list table in eloquent using laravel 5

I have this table with 4 id's related to other tables.
Table List:
id table1_id table2_id table3_id table4_id
1 2 3 2 1
2 1 4 3 3
I want now to output the values related to that table's ids
id table1_name table2_name table3_name table4_name
1 jay student singer actor
2 jeane teacher drummer actress
ListController:
public function index()
{
$res = ListModel::with('table1','table2','table3','table4')->get();
$foreach($res as r){
return $r->table1_name;
}
return view('list.index',compact('res'));
}
My problem here is that the output will be null instead of jay.How can I display now the values?Please help..
list table
public function up()
{
Schema::create('list_table', function (Blueprint $table) {
$table->increments('id');
$table->string('table1_id');
$table->string('table2_id');
$table->string('table3_id');
$table->string('table4_id');
$table->timestamps();
});
}
//additional info
Table1 Model
public function list(){
return $this->belongsTo('App\ListModel');
}
List Model
public function table1(){
return $this->hasMany("App\Table1",'id','table1_id');
}
Updated Values
id crime_type_id crime_name_id crime_suspect_id crime_victim_id
1 1 1 1 1
Expected output:
crime_type_des crime_name_des crime_suspect_name victim_name
against property theft Mcdonald Hillary
ReportController:
public function index()
{
$display_crime = CrimeReport::all();
return view('crimereports.index',compact('display_crime'));
}
report_table:
public function up()
{
Schema::create('crime_reports', function (Blueprint $table) {
$table->increments('id');
$table->string('crime_type_id');
$table->string('crime_name_id');
$table->string('suspect_id');
$table->string('victim_id');
$table->timestamps();
});
}
//additional info
CrimeName Model
public function crimeReport(){
return $this->belongsTo('App\CrimeReport');
}
Crime Report Model
public function crimeName(){
return $this->hasMany("App\CrimeName",'id','crime_name_id');
}
//Rest for CrimeReportController
public function index()
{
$display_crime = CrimeReport::all();
return view('crimereports.index',compact('display_crime'));
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
$display_crime_type = CrimeType::lists('crime_type','id');
$display_crime_name = CrimeName::lists('crime_description','id');
return view('crimereports.create',compact('display_crime_type','display_crime_name'));
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(CrimeNewReportRequest $request)
{
$input = $request->all();
CrimeReport::create($input);
return redirect()->back()->withSuccess("Fields were inserted!");;
}
Change your relation definition as below:
Table1 Model
public function list(){
return $this->hasMany('App\ListModel');
}
List Model
public function table1(){
return $this->belongsTo("App\Table1");
}
You need update your index method as below, you could dump $rows to check if this work.
ListController
public function index()
{
$res = ListModel::with(['table1','table2','table3','table4'])->get();
$rows = [];
$foreach($res as r) {
$rows[] = [r->table1->name, r->table2->name, r->table3->name, r->table4->name];
}
dd($rows);
}
Your accessing table1_name on the original model change the _ to -> ( $r->table1->name)
EDIT:
Now it's starting to become much more readable. Here are what I think the relationships should be.
CrimeReport.php
public function crimeType()
{
return $this->belongsTo(CrimeType::class);
}
public function crimeName()
{
return $this->belongsTo(CrimeName::class);
}
public function suspect()
{
return $this->belongsTo(Suspect::class);
}
public function victim()
{
return $this->belongsTo(Victim::class);
}
There really doesn't seem to be a need to define the inverse of any of there relationships right away, if the need arises later then add them but there is no point creating the relation just because its there.
Now to make showing the report easy lets use some joins instead or eager loading.
CrimeReportController.php
public function index()
{
$results = CrimeReport::query()
->join('crime_types as ct','ct.id', '=', 'crime_reports.crime_type_id')
->join('crime_names as cn','cn.id', '=', 'crime_reports.crime_name_id')
->join('suspects as s','s.id', '=', 'crime_reports.suspect_id')
->join('victims' as v','v.id', '=', 'crime_reports.victim_id')
->get([
'crime_report.*', //to get ids and timestamps
'ct.name as crime_type',
'cn.name as crime_name',
'suspect.name as suspent_name',
'victim.name as victim_name'
])
dd($results->toArray());
}
Now $results will still be a collection of CrimeReport so you can still have access to all the model goodies you might want to throw in there. In addition to that you also already have your table, you can just print these out into an html table and you have your report.
If you were wanted to add some cool search stuff to the query then you can use whereHas to do that:
//this would just go before the get()
$query->whereHas('suspect', function($q){
$q->where('hair_color','=','brown')
->whereBetween('height',5.8,6)
})
Of course you might also want to make some of those leftJoins instead since I assume they won't all have a victim/suspect

Resources