Laravel 4: Relation executing query using the wrong row - laravel-4

I have a relation I'm trying to get working that seems to be searching by the wrong column. I have a model, userwords, that should be getting an associated word with it. I want it to be using the word_id column from the userword table to search for a word by the id in the word table, but instead it seems to be using the id of the userword row to search for the word. I thought that perhaps that if I told it which column to use in the third parameter of hasOne() it would work, but to no avail. The code in question is:
public function word(){
return $this->hasOne('Word', 'id', 'word_id');
}
any help or ideas would be appreciated! Also if you need more information, please just let me know and i'll update this here! Thanks a lot!

Your parent table is userword and related child table is word, in this case, Userword model should contain following method for making the relation with word table:
class Userwords {
protected $table = 'userword';
public function word(){
return $this->hasOne('Word', 'userword_id'); // use the foreign key here
}
}
In this case, your word table should contain the userword_id as a foreign key. So, if you have a different foreign key defined word table then use that foreign key in the place of userword_id.
Also remember that, tables should use plural name of the word, for example, words should be the table name but you used word and the Model should use a singular name, for example, Word for the words table, so you have a different name convension here so use the protected $table = 'word' in your Word model and in Userwords model use protected $table = 'userword'. So, finally, it should be something like this:
class Userword {
// change the table name in database (use userwords)
protected $table = 'userwords';
public function word(){
return $this->hasOne('Word', 'userword_id'); // use the foreign key here
}
}
For words table, it should be:
class Word {
// change the table name in database (use words)
protected $table = 'words';
public function userwords(){
return $this->belongsTo('Userword');
}
}
Read the manual for more information about Laravel Model Relationships.

Related

Laravel - Model get data from another Model

I'm quite new to laravel and I'm trying to understand the Eloquent Relations.
I've already read some answers and the documentation but I haven't found a simple case similar to mine.
I have two model with one-to-many relation.
Document Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Document extends Model
{
public function dossiers()
{
return $this->belongsTo('App\Dossier');
}
protected $table = 'documents';
protected $primaryKey = 'id_document';
}
Dossier Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Dossier extends Model
{
public function documents()
{
return $this->hasMany('App\Document');
}
protected $table = 'dossiers';
protected $primaryKey = 'id_dossier';
}
So there is an attribute "protocol" inside both table. Inside Dossier is an unique attribute, inside Document can be repeated because more documents may be part of same dossier with that protocol.
Let's suppose I have to create a Document Model. But I need an attribute "color" that already exists in a Dossier.
so:
I have a form for the document. That can set all attribute except
color (so it can create the protocol shared with Dossier)
I submit the form then check if exists a Dossier with that protocol
(that is not the primary_key)
If i find a Dossier with that protocol, I took the Color of that dossier and
I insert it in the Document Model.
I'd like to know how I should implement the second step.
At the moment I've wrote in the DocumentController:
$document = Dossier::where('protocol', '=', $request->protocol)->first();
and then
$document -> color;
But I fell that's not the way.
Thanks for any advice
I think you want to insert color in document from dossier table if protocol match. If i understand you correctly then you can write it like this.
$dossier = Dossier::where('protocol', '=', $request->protocol)->first();
$color = ($dossier) ? $dossier->color: 'defaultColor';
Now in your create document
$document -> create([
'color' => $color,
... other form data here
]);
Note: it is not good practice to use primary key field as id_document or id_dossier. Better you change these to id, otherwise your current relationship will not work. Default, laravel relationship assume id as primary key. If you want to use different primary key name then you need to pass that key name in relationship as a second parameter.
$this->hasMany('App\Comment', 'foreign_key', 'local_key');
https://laravel.com/docs/5.6/eloquent-relationships#one-to-many
There are manyway to write the relation code, one is as rkj said anad u did as well.
The other one which i personally prefer is
$this->hasManny(Comment::class,'foreign_key', 'local_key'); //This is only if you are not following laravel standard table syntex.
But if your local key is id and foreign key on another table is comment_id. Then you dont need to add foreign key and primary key. You can simply do like this
$this->hasManny(Comment::class);
For making query, You can simply do this
$dossier = Dossier::where('protocol', $request->protocol)->first(); //This will give object
You do not need = , if its other then that u need to add that. This way its looks much cleaner right?
Then to display you can simply do this $dossier->color

Eloquent join with where clause

I have problems to build a relationship with eloquent.
I have two models created, Spielplan and Verein. In model Spielplan I have the fields Team_ID and Spiel_ID. In model Verein I have the field V_ID and Name. Now I need to join this two tables about Team_ID = V_ID.
This is my model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Spielplan extends Model
{
protected $table = 'Spielplan';
public function vereinFunction(){
return $this->hasOne('App\Verein', 'V_ID');
}
}
And this is a function in my web route where I want to get Spiel_ID and Name.
Route::get('test', function(){
$spieleT = App\Spielplan::where('Spiel_ID', '=', 30)->get();
foreach($spieleT as $da){
echo $da->Spiel_ID;
echo $da->vereinFunction->Name;
}
});
The first echo works and I get back Spiel_ID but the second echo gives back ErrorException Trying to get property of non-object.
What is wrong with my code?
Try editing this line:
$spieleT = App\Spielplan::with('vereInFunction')->where('Spiel_ID', '=', 30)->get();.
The with() allows you to fetch the association at the time you use get(). After using get(), you're working with a collection, and can't query the database again.
Try specifying the model primary key as a third argument, because if not, Laravel will assume it is named id, which is not the case.
Allow me to suggest you something: I used to name the tables and fields like you do (in the days I use Codeigniter) but since I started using Laravel around three years ago, I follow Laravel convention (which is recommended, but not imposed). I now name the tables in lowercase, (snakecase) plural, table fields also snakecasm lowercase. Models singular, camelcase similar corresponding table, relation function names as related model, being singular if relation is to one, plural if to many, etc. The advantage of this is among other reflected in model relationship declaration, which is a lot simpler and easier to define.
For instance (only as demonstration of stated above),
tables (with relation one to many:
managers (primarykey: id, name, ......)
technicians (primary key: id, foreingkey: manager_id (related table name in singular plus underscore plus id), name, .....)
models:
Manager:
/* relationships */
public function technicians () // see name as related table, plural due to as many relationship)
{
return $this->hasMany(Technician::class); // as naming convention has followed, you don't need to specify any extra parameters;
}
Techician:
/* relationship */
public function manager() // named as related table, singular due to to one relationship
{
$this->belongsToOne(Manager::class); // again, as naming convention has followed, you don't need to specify any extra parameters;
}
Therefore you can do this:
$manager::find(1);
echo $manager->technicians->first()->name,
or
foreach ($manager->technicians as $technician) {
echo $technician->name;
}
as well as:
$technician->manager->name;
Remember, a proper model relationship definition will save a lot of headache along the way, like the one you have
Hope this help in anyway

Many to many relationship returns empty set

I have a following table structure in my existing database :
tbl_Grzyby (ID, Nazwa, ...) - main table,
tblk_TypGrzyba (ID, TypGrzyba, OrderNo) - lookup table,
tblm_TypGrzyba (ID_Grzyb, ID_TypGrzyba) (many-to-many table).
I've created a grzyby Eloquent model class, which has a custom property, defined as:
class grzyby extends Model
{
protected $table = 'tbl_Grzyby';
public function typ_grzyba() {
return $this->belongsToMany('\App\Lookups\typ_grzyba', 'tblm_TypGrzyba','ID_Grzyb', 'ID_TypGrzyba');
}
}
I've also created the lookup table model, defined as:
class typ_grzyba extends Model {
protected $table = 'tblk_TypGrzyba';
public function grzyby() {
return $this->belongsToMany('\App\grzyby', 'tblm_TypGrzyba','ID_Grzyb', 'ID_TypGrzyba');
}
}
When I try with tinker to get record from main with ID=67, like:
$main_record=\App\grzyby::where('ID',67)->first();
it returns me all the details for that record fine. Returning all lookups runs also fine:
$types = \App\Lookups\typ_grzyba::all();
However, if I try to do the following:
$main_record_types = $main_record->typ_grzyba;
tinker returns me an empty collection, despite record existing in the many to many table:
When I run the query log it outputs me following SQL query:
select `tblk_TypGrzyba`.*, `tblm_TypGrzyba`.`ID_Grzyb` as `pivot_ID_Grzyb`, `tblm_TypGrzyba`.`ID_TypGrzyba` as `pivot_ID_TypGrzyba` from `tblk_TypGrzyba` inner join `tblm_TypGrzyba` on `tblk_TypGrzyba`.`id` = `tblm_TypGrzyba`.`ID_TypGrzyba` where `tblm_TypGrzyba`.`ID_Grzyb` is null
If I run that query against the database it returns me empty result, however, if I change the where clause manually to
where `tblm_TypGrzyba`.`ID_Grzyb` = 67
I'm getting the expected results in phpMyAdmin.
What's the reason of it and how I can fix that?
The solution was to add the primaryKey overrride because I was using uppercase for the ID field and then restart tinker.
You mixed up your foreign key defintion in your relationships. You wrote this twice:
return $this->belongsToMany('\App\grzyby', 'tblm_TypGrzyba','ID_Grzyb', 'ID_TypGrzyba');
I assume copy pasting? in the second model, justt swap the foreign key. Foreign key of the current model comes first.
In typ_grzyba model, do
public function grzyby() {
return $this->belongsToMany('\App\grzyby', 'tblm_TypGrzyba', 'ID_TypGrzyba', 'ID_Grzyb');
}

Laravel - Using different field names in the database (userid in a table and created_by in other)

I am trying to use a table for my Users and separate table for users' Projects in my database. However I want the names of the fields to be different for user id. What I want to take the id from the 'Users' table; and while saving the created project to the database, use that (user) id as created_by_id in Projects table.
public function store(CreateProjectRequest $request)
{
$project = new Project($request->all());
Auth::user()->projects()->save($project);
// Project::create($request->all());
return redirect('pages/home');
}
Also in Users.php, I added:
public function projects()
{
return $this->hasMany('App\Project');
}
The commented field is working on its own. However, I guess my problem arises because when I comment that line again, and add the other two lines ($project... and Auth::user... bits), I guess it is assuming I have a field in the Projects table named id.
I thought I would work around this problem with changing the primary key but I couldn't find how to take the Auth::user()->id; and make it write that value in created_by_id in a secure way. This is what I found though:
class Project extends Eloquent {
protected $primaryKey = 'created_by_id';
}
Edit: I don't think changing the primary key is my solution.
You can pass a second and third parameter to hasMany() method to specify the keys to use. Documentation
public function projects()
{
return $this->hasMany('App\Article','userid','created_by');
}

Use a different column on a many-to-many relationship in Laravel 4

I've got a situation in a project where I need to form a relationship between a primary key on one table and an indexed column (not the primary key) on another. Here's a sample of the database layout:
courses table
id
level
resources table
id
courses_resources table
course_level
resource_id
In my CourseResource model I have the following:
public function courses(){
return $this->belongsToMany('Course', 'courses_resources', 'resource_id', 'course_level');
}
Which works fine.
Then in my Course model I have:
public function resources(){
return $this->belongsToMany('CourseResource', 'course_resources', 'course_level', 'resource_id');
}
Which doesn't work. When I look at the last query performed on the database, it appears Laravel is searching the course_level column using the course's ID. That makes sense, but is there any way to use the level column for this relationship?
Eloquent BelongsToMany depends on PKs, so there is no way to do that with its methods.
You need custom relation object for this, that will check for given field, instead of primary key.
A quick and hacky solution would be this:
// Course model
public function getKey()
{
$relation = array_get(debug_backtrace(1, 2), '1.object', null);
if (method_exists($relation, 'getForeignKey')
&& $relation->getForeignKey() == 'courses_resources.course_level')
{
return $this->getAttribute('level');
}
return parent::getKey();
}
However if you would like to use it in production, do some extensive testing first.

Resources