I've two tables to save data to. One of them has foreign key so that I have one-to-many relationship. However, I don't understand how to save data into two table simultaneously. I have one query which contains data for one table and for another that should be attached to first one.
That is the main model
class Site extends Model
{
protected $fillable = ['path', 'site_link'];
public $timestamps = false;
public function features() {
return $this->hasMany('App\SiteFeature');
}
}
And this is the sub-model
class SiteFeature extends Model
{
protected $fillable = ['feature', 'site_id'];
public $timestamps = false;
}
Right now my controller looks like this
class SiteController extends BaseController
{
public function index()
{
return Site::all();
}
public function show(Site $id)
{
return $this->response->item($id, new SiteTransformer);
}
public function store(Request $request)
{
$site = Site::create($request->all());
return response()->json($site, 201);
}
I know that it would save it as one piece of data. And I ask you for help me to split data into two tables. In docs I've found the way to store with relationship to an existing model in DB, however I don't have that model at the moment of creation.
Solved that way
public function store(Request $request)
{
$site = Site::create([
"path" => $request->path,
"site_link" => $request->link,
]);
foreach ($request->features as $feature) {
$site->features()->save(new SiteFeature(["feature" => $feature]));
}
return response()->json($site, 201);
}
There are certain things you have to make sure of.
First: In your SiteFeature-Model the inverse relation to the Site-Models seems to be missing.
There should be a function like:
public function site()
{
return $this->belongsTo('App\Site');
}
See Laravel 5.6 - Eloquent Relationships, One-to-Many for this.
If however you have a relationship where (n) Sites can be related to (n) SiteFeatures, your relations and inverse relations have to be different.
(And there will also have to be a pivot table, in which you can store the n-to-n relation)
See Laravel 5.6 - Eloquent Relationships, Many-to-Many in that case.
Since your Question does not describe what is received with $request, here's what you should consider:
Validate your inputs. This will make sure you don't save garbage to your database
Check if you already have some part of the data-set you want to save, then save in two steps:
First step:
$site = Site::firstOrCreate(['path' => $request['input_name_for_path'],
'site_link' => $request['input_name_for_site_link'],
]);
This will give you a proper Site-Model saved to the database.
(Note, that this shows how you manually assign values to the fillable fields defined in the model in case you have different input field names)
Now you can go on an save the SiteFeature-Model connected to it:
$feature = SiteFeature::firstOrCreate('feature' => $request['input_name_for_feature');
$site->features()->attach($feature->id);
This should do the trick saving both, a new (or old) Site and a related SiteFeature to your database.
If I misunderstood the question, feel free to add information and I will update.
It's the correct way to save data using hasMany relationship without creating a new object of lookup model.
// inside controller
public function store(Request $request)
{
$student = Student::create([
"name" => $request->get('name'),
"email" => $request->get('email'),
]);
foreach ($request->subjects as $subject) {
$student->subjects()->create(["title" => $subject['title']);
}
return response()->json($student, 201);
}
// inside User model
public function subjects()
{
return $this->hasMany('App\Models\Subject');
}
Related
I have a table named reports in which i have two columns. One is reported_post_id and the other is reporter_id. In reporter_id the value can be more than 1 and it's json. I want to make a relationship between the unique reported_post_id with the many reporter_id which is already saved in column as json data.
public function reported() {
return $this->hasMany('App\Models\Repoters', 'reported_post_id');
}
public function reporters() {
return $this->belongsTo('App\Model\Repoters', 'reporter_id');
}
$column = [\DB::raw("*"), \DB::raw("id as postable_id"),\DB::raw("report_type as postable_type")];
$data = Repoters::select($column)->with("repos")->orderBy('id', 'DESC')->paginate(50)->unique("reported_id");
I want that to show all data of reported_post_id with reporter_id.
You could make use of the eloquent-json-relations package.
Install it:
composer require staudenmeir/eloquent-json-relations:"^1.1"
Then in your User model (or the relevant one):
class Repoters extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function reported() // or the name that you want
{
return $this->hasManyJson('App\Model\Repoters', 'reporter_id');
} // ^^^^^^^^^^^the json column.
public function reporter() // or the name that you want
{
return $this->belongsTo('App\Model\Repoters', 'reported_post_id');
}
}
From the package documentation:
Many-To-Many Relationships
This package also introduces two new relationship types:
BelongsToJson and HasManyJson
On Laravel 5.6.25+, you can use them to implement many-to-many
relationships with JSON arrays.
In this example, User has a BelongsToMany relationship with
Role. There is no pivot table, but the foreign keys are stored as an
array in a JSON field (users.options):
Array of IDs
By default, the relationship stores pivot records as an array of IDs:
class User extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
protected $casts = [
'options' => 'json',
];
public function roles()
{
return $this->belongsToJson('App\Role', 'options->role_ids');
}
}
class Role extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function users()
{
return $this->hasManyJson('App\User', 'options->role_ids');
}
}
So, this is weird. I have been implementing ontoMany relationships between users and various data sets. The first one worked... sort of. I set up the pivot table and what not, the data is correct on both ends of the table but the result when laravel calls the data is not even close:
Let's take this show user data function:
public function show($id)
{
try {
$loc = UserEdit::findorFail($id);
$array = UserEdit::findorFail($id)->toArray();
//$prefs = Ministry_Prefs_User::find($id);
return view('UserEdit', compact('array', 'loc'));
} catch(\Exception $e) {
return \Redirect::route('users.index')
->withMessage('This user does not exist');
}
}
In the blade I print out the tags for their preferences:
Preferences:<br>
{{ $array['Preferences'] }}<br>
#foreach ($loc->prefs_user as $tag)
{{ $tag->tag }}<br>
#endforeach<br><br><br>
The first array prints what's stored in the original untouched user data from the old table I inherited. That's so I can compare and make sure I'm getting the right data from the pivot tables I had to generate from this info. Turns out that was a good idea, coz this is what's printing:
1A-4,1-2,1-3,2-3,3-6,4-7,6-11,8-6,8-10,9-4,7A-4
1A-1
1A-1
1A-1
1A-3
1A-4
1A-5
1A-7
1-1
1-1
1-6
1A-8
I can see no pattern other than alphabetical order? Why would this happen?
The next point is even weirder. Using the same code get's me no results at all from the other pivot tables I've set up:
public function country() {
return $this->belongsToMany('App\Country', 'country_user', 'user_id', 'country_id');
}
public function prefs_user() {
return $this->belongsToMany('App\Prefs_User', 'tag_user', 'user_id', 'tag_id');
}
public function language() {
return $this->belongsToMany('App\language', 'language_user', 'user_id', 'language_id');
Why would this happen? The models look like this:
Prefs_User
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Prefs_User extends Model
{
protected $table = 'prefs';
public function prefs_user() {
return $this->belongsToMany('App\UserEdit', 'tag_user', 'tag_id', 'user_id');
}
}
Country
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
protected $table = 'countries';
public function country_user() {
return $this->belongsToMany('App\UserEdit', 'country_user', 'country_id', 'user_id');
}
//protected $fillable = ['region', 'country'];
}
language
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class language extends Model
{
protected $table = 'languages';
//protected $fillable = ['user_id','language_id'];
public function language() {
return $this->belongsToMany('App\UserEdit', 'language_user', 'language_id', 'user_id');
}
}
Ok. So this was a problem caused by a function of how Laravel works.
After some trial and error, I made a guess that Laravel only checks for id's when using pivot magic methods and does not search for any other data.
So I wrote a function to get the preferences, explode them out into the original tags, then compare that tag against the preference list, get the id associated with that tag and then print it to the pivot table with the other data. For testing and double checking purposes the original tag was initially included, then removed later.
public function move() {
//get the preferences from the users table (this is stored badly "1-7,3-4,4-6,6-6,6-10,8-5,8-9,8-10,8-13,9-3,9-9") this returns a collection
$prefs = DB::table('users')->select('id', 'Preferences')->where('Preferences', '!=', '')->get();
//iterate through each item in the collection
foreach ($prefs as $pref) {
//get the tags only
$tags = $pref->Preferences;
//break up the tags into a searchable array by exploding each preference at the comma
$tag = explode(',', $tags);
//for each tag in the array assign it to t
foreach ($tag as $t) {
//get the id from the preference table when the tag matches t
$new = DB::table('prefs')->select('id')->where('tag', $t)->first();
//make a new model instance to insert data
$taguser = new Tag(array(
'user_id' => $pref->id,
'tag_id' => $t,
'tags' => $new->id
));
//insert data
$taguser->save(); //save in table
//dd($taguser); <- Since we have a lot of items, dd will break the printout after the first, meaning you can check to see if your information is being handled properly
}
}
return view('refactor');
getting rid of the original tag-id and replacing it with just the plain id that referenced the tag meant that laravel could now search against the correct data and return useful results.
i am trying to save data in third table in many to many relation but
data is not saving
user model
public function Jobs()
{
return $this->belongsToMany('App\Models\Job','App\Models\Job_User','user_id','job_id');
}
job model
public function Users()
{
return $this->belongsToMany('App\Models\User','App\Models\Job_User','job_id','user_id');
}
controller for saving data in third table is
public function JobApplied(Request $request){
$applied= new Job_User();
$applied->user_id = Auth::id();
$applied->job_id = $request->job_id;
$applied->cv = $request->cv;
$applied->current_salary = $request->current_salary;
$applied->expected_salary = $request->expected_salary;
$applied->save();
return redirect('searchjobs');
}
code of third table is
class Job_User extends Model
{
protected $fillable = [
'user_id','job_id','cv','current_salary','expected_salary','status',
];
protected $table = 'jobs_users';
}
You're using the many-to-many relation incorrectly. You don't need a model for the intermediate table as Eloquent will handle it for you.
First of all, you need to define the relation in your models in a correct way. The second argument should be the name of the intermediate table, not the model. As you're using the default values for table name and foreign key column names, you can skip them and just do:
public function Jobs()
{
return $this->belongsToMany('App\Models\Job');
}
public function Users()
{
return $this->belongsToMany('App\Models\User');
}
If you want to have additional fields in the intermediate column, you need to define it when you define a relationship using withPivot() method:
public function Jobs()
{
return $this->belongsToMany('App\Models\Job')->withPivot('cv','current_salary','expected_salary','status');
}
public function Users()
{
return $this->belongsToMany('App\Models\User')->withPivot('cv','current_salary','expected_salary','status');
}
Now, if you want to link a Job with a User and set the fields in the intermediate pivot table, you should use save() method on your relation:
$job->users()->save($user, ['cv' => $request->cv, 'current_salary' => $request->current_salary]);
or
$user->jobs()->save($job, ['cv' => $request->cv, 'current_salary' => $request->current_salary]);
Once you have data saved in your database you can retrieve data from intermediate pivot table using the pivot attribute of related model, e.g.:
foreach($user->jobs as $job) {
echo $job->pivot->current_salary;
}
or
foreach($job->users as $user) {
echo $user->pivot->current_salary;
}
Check the docs for more information about handling many-to-many relationship with Eloquent: https://laravel.com/docs/5.1/eloquent-relationships#many-to-many
I have this problem. I have a Group and Role models, with a Many-to-Many relationship setup.
Group model
public function roles()
{
return $this->belongsToMany('App\Role', 'group_roles');
}
Role Model
public function groups()
{
return $this->belongsToMany('App\Group', 'group_roles');
}
GroupsController store method
public function store(Request $requests)
{
$group = new Group;
//$group->group_name = $requests->group_name;
//$group->save();
$group->create($requests->all());
$group->roles()->sync($requests->input('roles'));
Session::flash('success', $requests->group_name.' successfully added');
return redirect('/settings/groups/groups');
}
The problem I have here is that when I call create method on the group model, it returns null, thus causing this $group->roles()->sync($requests->input('roles')); to fail. However, when I use the save method, it works flawlessly. Why doesn't the create work?
EDIT: if I use create it does insert the records into the database, but the problem is that it's not return insertLastId.
In Group Model
public function groups()
{
protected $fillable = ['group_name'] //add all the fillable fields
return $this->belongsToMany('App\Group', 'group_roles');
}
When create() method is used, it mass assigns all the values, so protected $fillable = [] should be used in that particular Model. see https://laravel.com/docs/5.1/eloquent#mass-assignment
For Last Insert Id use db function instead because create() method doesn't return lastInsertId, it only returns true if data inserted successfully.
return DB::('table_name')->insertGetId([
'group_name' => 'some name'
]);
I have figured it out. I changed this:
$group = new Group;
$group->create($requests->all());
To
$group = Group::create($requests->all())
Now create returns the last insert ID.
When I want to save a reaction to a ticket I've three tables (in laravel):
-Tickets
-Reaction
-Reaction_Tickets (pivot table)
When I want to save a reaction I do it like this:
public function addReaction(Request $request, $slug)
{
$ticket = Ticket::whereSlug($slug)->firstOrFail();
$reaction = new reactions(array(
'user_id' => Auth::user()->id,
'content' => $request->get('content')
));
return redirect('/ticket/'.$slug)->with('Reactie is toegevoegd.');
}
But now of course it's not added to the pivot table. And I can't add it because I don't have a model of it. What's the right way of doing this?
EDIT:
-Tickets
public function reactions()
{
return $this->belongsToMany('App\reactions');
}
-Reactions
public function tickets()
{
return $this->hasOne('App\tickets');
}
From the Laravel documentation, you need to save and attach the Reaction to the Ticket:
$reaction = new reactions(array(
'user_id' => Auth::user()->id,
'content' => $request->get('content')
));
$reaction->save(); // Now has an ID
$tickets->reactions()->attach($reaction->id);
In your Ticket model, you need to have the relationship defined:
class Ticket extends Model {
protected $table = "tickets";
public function reactions(){
return $this->belongsToMany("App\Reaction");
}
}
And you should have the inverse defined on Reaction:
class Reaction extends Model {
protected $table = "reactions";
public function tickets(){
return $this->belongsToMany("App\Ticket");
}
}
If your models are set-up like so, you shouldn't have any issue attaching the new Reaction to your existing Ticket via your pivot table.