Laravel 5 Many to Many - Table name in singular - php

MySQL Tables:
- category
- unit
- category_unit (many to many)
- category_id
- unit_id
Laravel 5 Models:
<?php
class Unit extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'unit';
}
class Category extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'category';
public function units()
{
return $this->morphToMany('App\Unit', 'category_unit'); // Table not in plural.
}
}
Controller Code:
$category = Category::find($id);
var_dump($category->units);
Error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'database.category_units' doesn't exist (SQL: select `unit`.*, `category_units`.`category_unit_id` as `pivot_category_unit_id`, `category_units`.`unit_id` as `pivot_unit_id` from `unit` inner join `category_units` on `unit`.`id` = `category_units`.`unit_id` where `category_units`.`category_unit_id` = 1 and `category_units`.`category_unit_type` = App\Category)
Laravel 5 is trying to find the table category_unit as the plural category_units. As my database is not new and I already used it in production servers, I cannot change the table name.
How can I do to Laravel 5 use it with singular name?

The problem here is that you are trying to create Many to Many relationship using a polymorphic one.
The morphToMany() method doesn't take the table name as the second argument. I think your case is simpler, just change the relation to belongsToMany()
So your code should be
class Category extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'category';
public function units()
{
return $this->belongsToMany('App\Unit', 'category_unit'); // Table not in plural.
}
}

Related

Table name won't change even after set in Laravel eloquent?

OrderProducts Eloquent:
class OrderProduct extends Model
{
use HasFactory;
protected $table = 'order_products';
}
I create a model for my table name called order_products.
My Eloquent name is OrderProduct. Then I change the table name in Eloquent to protected $table = "order_products"`.
Still, I'm getting order_product table doesn't exist issue?
Why?
Changing the Model would not automatically change the table name within your DB
You can specify a custom table name by defining a table property on your model:
class OrderProduct extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'order_products';
}
Ref: https://laravel.com/docs/8.x/eloquent#table-names

Laravel - BelongsToMany relation ignores my custom (singular) table name

I am working on a older (laravel 5.6) codebase, this codebase has some models with singular table names (not by my design..). When I setup a new pivot relation between my tables "m_sector" and "m_tasklist" Eloquent automatically assumes that the table name of "m_tasklist" is plural; "m_tasklists". I know this is by Laravel's design, therefor I use a manual override defined in the Tasklist model. After changing the $table property to `protected $table = 'potato'; the changes were detected and implemented in the query..
Error message
"debugMessage": "SQLSTATE[42S02]: Base table or view not found: 1146 Table 'database.m_tasklists' doesn't exist (SQL: select count(*) as aggregate from `m_tasklists` where `id` in (1, 2, 3))"
Tasklist Model
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'm_tasklist';
/**
* A task list belongs to many sectors.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function sector(): BelongsToMany
{
return $this->belongsToMany(Sector::class, 'm_sector_m_tasklist', 'm_tasklist_id', 'm_sector_id');
}
Sector Model
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'm_sector';
/**
* A sector belongs to many task lists.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tasklists(): BelongsToMany
{
return $this->belongsToMany(Tasklist::class, 'm_sector_m_tasklist', 'm_sector_id', 'm_tasklist_id');
}
Picture of PhpMyAdmin table names
Can anyone please help me figure this out, It has been breaking my head for a day now.
If anyone would like to know, the key constraints have formed correctly the migrations work and are set up properly. I can add them if it helps.
The issue wasn't with the model. My usecase had a validation rule which checked whether the item existed in m_tasklists instead of m_tasklist
'rules' => ['exists:m_tasklists,id']
instead of the correct
'rules' => ['exists:m_tasklist,id']
I recommend everyone with a similar problem to run
$ php artisan tinker
$ (new Model)->getTable();

Eloquent hasMany Not Returning Any Results

I have a noob question about how Eloquent generates SQL for the following.
DB:
Customer Table:
ID (Public Key)
... (Some general columns)
Group Table:
ID (Public Key)
FK_CUSTOMER_ID (Foreign Key)
... (Some general columns)
I have the following code in Customer Model:
public function groups()
{
return $this->hasMany(Group::class, 'fk_customer_id');
}
I am trying to get all groups (in the groups function above), that I can narrow down the groups later, to a particular customer.
The above code generates the following SQL (which results an empty result set, which is understandable by looking at the SQL). I have no idea, why the where clause (see the SQL below) gets generated, does not makes much sense.
select * from `group` where `group`.`fk_customer_id` is null and `group`.`fk_customer_id` is not null limit 1
I would like the following SQL to be generated :
select * from `group`
also, how to get the following SQL generated (If I need to select groups based on customer_id, I believe I'll need to add some where clause somehow)
select * from `group` where `group`.`fk_customer_id`= SOME_VALUE
Thanks!
--- Customer Model
<?php
namespace App;
use App\Role;
use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\Model;
class Customer extends Model
{
/**
* Get ALL groups for the customer.
*/
public function groups()
{
return $this->hasMany(Group::class, 'fk_customer_id');
}
/**
* Get ONE group for the customer.
*/
public function group($groupId)
{
return $this->hasMany(Group::class, 'fk_customer_id')->where('id', $groupId);
}
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'customer';
}
Group Model
<?php
namespace App;
use App\Group;
use App\Customer;
use Illuminate\Database\Eloquent\Model;
class Group extends Model
{
/**
* Get ONE customer for the group.
*/
public function customer()
{
return $this->belongsTo(Customer::class, 'fk_customer_id');
}
/**
* Get ONE directory configuration for the group.
*/
public function directoryConfiguration()
{
return $this->belongsTo(DirectoryConfiguration::class, 'fk_directory_configuration_id');
}
/**
* Get ONE user for the group.
*/
public function user($userId)
{
return $this->hasMany(User::class, 'fk_role_id')->where('user_id', $userId);
}
/**
* Get ALL user for the group.
*/
public function users()
{
return $this->hasMany(User::class, 'fk_role_id');
}
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'group';
}

Relations Laravel 4 with 3 tables using Eloquent

I want to make a relation with 3 table using ORM but cant. My tables
User table
id | userame | name |
1 Ellie Elen
2 Pol Paul
record table
id | user_id| item_id| hour|
1 2 1 3
2 2 2 5
Item table table
id | title
1 item 1
2 item 2
I am using this logic but not work properly
class User Extends Eloquent {
public function record()
{
return $this->hasMany('VolunteerRecord');
}
}
class VolunteerRecord Extends Eloquent {
function item() {
return $this->hasMany('VolunteerItem');
}
}
I cant understand how to do it?
It seems like you want a Many-To-Many relationship between Users and Items but you also want to track hours on the pivot table. So first, you'll define the many-to-many relationships using belongsToMany(), and you'll tell Laravel that you have extra data on your pivot table with the withPivot() function. Your classes will look like this:
class User extends Eloquent {
protected $table = 'users';
public function items() {
return $this->belongsToMany('Item', 'records')->withPivot('hour');
}
}
class Item extends Eloquent {
protected $table = 'items';
public function users() {
return $this->belongsToMany('User', 'records')->withPivot('hour');
}
}
Then, to access the hour field you would do this:
$user = User::first(); // First User
$item = $user->items()->first(); // User's first Item
$hour = $item->pivot->hour; // The 'hour' on the User-Item relationship
Also, your current column naming scheme is correct for Laravel so don't feel like you need to change it. If you change your column names, then you'll need to define them in the belongsToMany() method like this:
$this->belongsToMany('ModelName', 'pivot_table', 'foreign_key', 'other_key');
// For example, in Items::users() you would have this:
$this->belongsToMany('User', 'records', 'users_id', 'items_id');
Finally, I'm assuming that your tables are named users, items, and records. If they are not, then just replace all instances of users, items, and records with your actual table names.
Based on your table names, I'd suggest the following, first of all, change your record table as follows:
id | users_id| items_id| hour|
1 2 1 3
2 2 2 5
And these are the classes for your models:
class Users extends Eloquent
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
public function records()
{
return $this->hasMany('Records');
}
}
class Records extends Eloquent
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'records';
public function record()
{
return $this->hasOne('Users');
}
public function item()
{
return $this->hasOne('Items');
}
}
class Items extends Eloquent
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'items';
public function records()
{
return $this->hasMany('Records');
}
}
These contain the relations for your models.
If you were to select a few records, for each record you can get the user and the item. If you were to select an item, and all records for that item. You can also get the user for each record.
In User Model
public function images()
{
return $this->belongsToMany('Item')->withPivot('hour');
}
In user controller
public function view($username)
{
$user = User::where('name',$username)->firstOrFail();
return View::make('view')->with('user',$user);
}
In view
#foreach ($users->items as $item)
name: {{$image->title}}<br>
hour: {{$image->pivot->hour}}<br>
#endforeach

What is the correct way of configing Eloquent globally?

Eloquent has some assumptions regarding db tables.
It uses plural name of class for tables name. I use singular nouns for tables' name.
By default, Eloquent expects created_at and updated_at columns. I use cData and uDate
I use camelCase to name columns not underline_separated names.
I know it possible to use class properties to overwrite those. What is the correct way to config it globally?
Instead of extending the Model class create a new class for example MyModel and setup your attributes there. Then extend MyModel
The best way is to create your own base model that extends Eloquent. Then you can define new values for the timestamp columns and override the getTable() method:
class BaseModel extends Model {
const CREATED_AT = 'cData';
const UPDATED_AT = 'uData';
/**
* Get the table associated with the model.
*
* #return string
*/
public function getTable()
{
if (isset($this->table)) return $this->table;
return str_replace('\\', '', snake_case(class_basename($this)));
}
}
Then, let all your models extend that base model:
class User extends BaseModel
you can do this by editing vendor\framework\src\Illuminate\Database\Eloquent\Model.php file.
/**
* The name of the "created at" column.
*
* #var string
*/
const CREATED_AT = 'cDate';
/**
* The name of the "updated at" column.
*
* #var string
*/
const UPDATED_AT = 'uDate';
/**
* The name of the "deleted at" column.
*
* #var string
*/
const DELETED_AT = 'dDate';
But honestly speaking, editing the core source is not a good idea. So you can do this by
class MyModel extends Model {
/**
* The name of the "created at" column.
*
* #var string
*/
const CREATED_AT = 'cData';
/**
* The name of the "updated at" column.
*
* #var string
*/
const UPDATED_AT = 'uData';
/**
* The name of the "deleted at" column.
*
* #var string
*/
const DELETED_AT = 'dDate';
}
Now write you model extending MyModel instead of original Model.
for example:
class User extends MyModel{
//Yes, if you want to change default table name then you should do this. Shouldn't be global.
protected $table = "user";
}

Categories