I want to change the default database naming conventions in my Laravel app. By default, Laravel uses snake case for database table and column names. But I want to use Pascal Case for table names and i want to use camel Case for fields.
So a table name of Users instead of users, and field names createdAt, updatedAt, and deletedAt instead of created_at, updated_at, and deleted_at.
I know I can change these on a per-model basis using the $table property but I'd like to change the default without having to modify each model.
Are there any settings like Symfony's NamingStrategy in Laravel?
If you look at the code for Illuminate\Database\Eloquent\Model::getTable() it's pretty straightforward:
public function getTable()
{
return $this->table ?? Str::snake(Str::pluralStudly(class_basename($this)));
}
Same for Illuminate\Database\Eloquent\Concerns\HasTimestamps::getCreatedAtColumn():
public function getCreatedAtColumn()
{
return static::CREATED_AT;
}
So create your own class that extends Model and override that behaviour:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model as BaseModel;
use Illuminate\Support\Str;
class Model extends BaseModel
{
const CREATED_AT = 'createdAt';
const UPDATED_AT = 'updatedAt';
const DELETED_AT = 'deletedAt';
public function getTable()
{
return $this->table ?? Str::pluralStudly(class_basename($this));
}
}
Now, just have your models extend this class.
You can use stubs for that. Also if you named your table in Pascal Case just use $table property in model which need to be sync with that table.
You can create a new model that you extend your models with.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class ModelWithPascalCase extends Model
{
const DELETED_AT = 'deletedAt';
const CREATED_AT = 'createdAt';
const UPDATED_AT = 'updatedAt';
public function getTable()
{
return $this->table ?? Str::pluralStudly(class_basename($this));
}
}
If you want to make Laravel generate your models extending this, you can do it by editing stubs.
Run
artisan stub:publish
then edit stubs/model.stub by replacing Model with your ModelWithPascalCase.
After that, when you run
artisan make:model User
you get your User model extended by ModelWithPascalCase.
Related
I have a navigation model that can have many items associated with it:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use JetBrains\PhpStorm\ArrayShape;
use Laravel\Scout\Searchable;
class Navigation extends Model
{
use HasFactory;
use Searchable;
protected $guarded = [];
public function navigation_items(): HasMany
{
return $this->hasMany(NavigationItem::class);
}
}
The navigation item model looks like this
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class NavigationItem extends Model
{
use HasFactory;
protected $guarded = [];
public function navigation(): BelongsTo
{
return $this->belongsTo(Navigation::class);
}
public function navigatable(): MorphTo
{
return $this->morphTo();
}
}
Now an item can either be of type Page or Blog, in this case the Page model looks like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use JetBrains\PhpStorm\ArrayShape;
use Laravel\Scout\Searchable;
class Page extends Model
{
protected $guarded = [];
public function navigatable(): MorphOne
{
return $this->morphOne(NavigationItem::class, 'navigatable');
}
}
When I try to save a navigation model and associate it with a item, the following error appears:
SQLSTATE[HY000]: General error: 1364 Field 'navigatable_type' doesn't have a default value
I save the model like this:
foreach ($this->selected as $id) {
$this->navigation->navigation_items()->create([
'navigation_id' => $this->navigation->id,
]);
Where $this->selected is the navigation id, it should automatically get the correct navigatable_type and navigatable_id, but this doesn't seem to be working.
passing in the type and id manually works, but this kinda defeats the point of a polymorphic relationship.
any ideas?
On NavigationItem model, since you defined polymorphic relation as 'navigatable' it is expected that NavigationItem model's table contains navigatable_type and navigatable_id. First please ensure this checks out.
Creating records through relation's base function is not a valid method. It is not clear what you are trying to achieve there but when you want to set relation there is two standard way of achieving it:
1- Associate
When a relation is defined as belongsTo, you may use associate() function. Like so:
$account = Account::find(10);
$user->account()->associate($account);
2- Attach
Attach is used when relation is defined belongsToMany (pivot). It allows you to attach multiple records to a model instance/record.
$user = User::find(1);
$user->roles()->attach($roleId);
So if you want to set a 'navigatable' to a Navigation instance, you may:
$somePageInstance=Page::find(55);
$nagivation->navigatable()->associate($somePageInstance)
$nagivation->save();//remember to save, otherwise it won't be
Im new to laravel, i am trying to query a specific table in my DB. I only have 1 data table and the standard user auth tables. I am getting a error: BadMethodCallException
Call to undefined method App\Figures::table().
Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Figures extends Model
{
}
controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Figures;
class figuresController extends Controller
public function figurespag2() {
$dummyDetails = Figures::table('figures')->where('name', 'batman');
return view ( 'pagination2.index' )->withUsers($dummyDetails);
}
route
Route::get ( '/pagination2', 'figuresController#figurespag2' );
I know it's going to be something obvious, but I am new to this.
this is wrong
$dummyDetails = Figures::table('figures')->where('name', 'batman');
Method 1---------- laravel eloquent
Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Figures extends Model
{
protected $table = 'figures';
}
Controller
$dummyDetails = Figures::where('name', 'batman')->get();
and
Method 2 ---------- laravel Query Builder
$dummyDetails = \DB::table('figures')->where('name', 'batman')->get();
Use this you not need to define table name
public function figurespag2() {
$dummyDetails = Figures::where('name', 'batman')->get();
return view ( 'pagination2.index' )->withUsers($dummyDetails);
}
First you may need to know laravel model rules.
If you create a table name like "figures" (plural) you need to create its model by Figure (singular).
if you create a table other then this rule then you have to mentioned table name in model like this.
protected $table = "table_name";
you can access table with where condition in controller like this.
public function figurespag2() {
$dummyDetails = Figure::where('name', 'batman')->get();
return view ( 'pagination2.index' )->withUsers($dummyDetails);
}
Hope this may help you.
I have three relational table attached below.
https://drive.google.com/file/d/1q1kdURIwFXxHb2MgdRyBkE1e3DMug7r-/view?usp=sharing
I have also three separate models where defined relation among all of my table's.I can read the City Model's information from Country model using hasManyThrough() relation But cannot read the Country information from City model. I have tried to retrieve City model's using ``hasManyThrough``` but didn't get result (attached as commented country method ). Please read my model and it's relational method here..
Is there someone to help me for getting City model's information using Eloquent method hasManyThrough / hasManyThrough or using inverse of hasManyThrough / hasManyThrough ?
01.
<?php
namespace App\Hrm;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Country extends Model
{
//use SoftDeletes;
protected $fillable = ['name','description','status'];
public function districts(){
return $this->hasMany(District::class);
}
public function cities(){
return $this->hasManyThrough(City::class,District::class);
}
}
02.
<?php
namespace App\Hrm;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class District extends Model
{
//use SoftDeletes;
protected $fillable = ['country_id','name','description','status'];
public function country(){
return $this->belongsTo(Country::class);
}
public function cities(){
return $this->hasMany(City::class);
}
}
3.
namespace App\Hrm;
use App\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class City extends Model
{
//use SoftDeletes;
protected $fillable = ['district_id','name','description','status'];
public function district(){
return $this->belongsTo(District::class);
}
// public function country(){
// return $this->hasOneThrough(Country::class, District::class);
// }
Doesn't look like there is a native way to define the inverse of a "hasManyThrough" relationship yet in Laravel. There have been a few issues opened on github to request it, but they were closed.
You could use the staudenmeir/belongs-to-through package if you don't mind installing a third-party package for this functionality. Then you should be able to define a belongsToThrough relationship like this:
class City extends Model
{
use \Znck\Eloquent\Traits\BelongsToThrough;
public function country() {
return $this->belongsToThrough(Country::class, District::class);
}
}
Why can't use parent method?
$city = City::find(1);
$country = $city->district->country();
i just had a similar situation i was able to accomplish a belongsToThrough with hasOneThrough
public function country()
{
return $this->hasOneThrough(
Country::class, // model we are trying to get
District::class, // model we have an _id to
'id', // WHERE `district`.`id` = `city`.`district_id`
'id', // `countries`.`id`
'district_id', // local column relation to our through class
'country_id' // `district`.`country_id`
);
}
what this should generate is
SELECT * FROM `countries`
INNER JOIN `districts`
ON `districts`.`country_id` = `countries`.`id`
WHERE `districts`.`id` = ?
-- ? == city.district_id
Database structure:
City:
id: increments
district_id: integer
...
Country:
id: increments
...
District:
id: increments
country_id: integer
...
we can then do $city->country
note: i have not fully tested this but with the testing that i have done it 'works'
Edit: i originally thought that i needed to leave the localKey
parameter null otherwise the relation wont work. it turns out i didnt
fully understand what that column was doing and that was wrong. That
key is the local column that relates to our through column (unless i
still have more to learn/figure out), when left the value as null, it
would use the local id column which a. is the wrong value, b. can also
be out of range (which is how i discovered it was using the wrong
value)
in my testing i only had two rows, both with the same relations. what
i didnt realize though was that on the "through table" both row 1 and
2 and the same related (relation where are trying to reach) so i didnt
notice the issue right away. hopefully now its all working
I am new to Laravel and also asked the question on Laracast without any success so far.
Here is my problem: I have a database layout something like this:
Table: categoryA_products
Table: categoryB_products
Table: categoryC_products
and per default the Laravel user table:
Table: user
I have create a two Laravel Eloquent models:
Product:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
// protected $table = '';
public function users()
{
return $this->belongsTo( User::class );
}
}
User:
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function products()
{
return $this->hasMany( Product::class );
}
}
As each product has a different table name I would normally create 1 model for each table but as they are all similar I would like to define the model table name at runtime.
I know I can do this with "$product->setTable()" but as I use the "newRelatedInstance" class from Laravel (hasMany and belongsTo) I cannot initiate the product class and set the table.
Is there a workaround for this?
Yes, I am aware that I could create a category table and link the products to each category but this is a fictional database model. There is a reason for this approach and I can explain it more in detail if needed. That said it make sense for this sample but I cannot use it for the live database.
I have a working solution with a model for each "category" but this is very messy.
Any help would be appreciated.
Since you're unable to load the relations, you could try referencing and re-initializing them like:
$relations = $product->getEagerLoads();
$attributes = $product->getOriginal();
table_name = 'categoryA_products'; // or categoryB_products or categoryC_products
$product->newQuery()
->newModelInstance($attributes)
->setTable($table_name)
->setEagerLoads($relations)
->...
I have a table named tasks and a model named Task. In my controller method when i run this piece of code
<?php
namespace App\Http\Controllers;
use DB;
//use app\Task;
use DateTime;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
class Task_Controller extends Controller
{
//
public function decide()
{
if ($input=="show all task")
{
//$rows=DB::table('task')->get();
$rows=\App\Task::all();
foreach($rows as $values)
{
foreach($values as $key=>$val)
echo "$key : $val <br>";
echo "<br><br>";
}
}
}
It gives me following error:
SQLSTATE[HY000]: General error: 1 no such table: tasks (SQL: select * from "tasks")
That is the model should have linked to the task table instead it is linked to the tasks table Why??.
How to make model gets linked to a specific table.
My model class code goes like this
namespace App;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
//
}
You should name your table tasks, this is the best option.
Another way to fix this is to use $table property:
protected $table = 'task';
Note that we did not tell Eloquent which table to use for our Flight model. By convention, the "snake case", plural name of the class will be used as the table name unless another name is explicitly specified. So, in this case, Eloquent will assume the Flight model stores records in the flights table. You may specify a custom table by defining a table property on your model
https://laravel.com/docs/5.4/eloquent#defining-models
By default, the table name is the plural of the class name. If you want it to point to a different table, then in your model, add
protected $table = 'task';
By default laravel pluralizes the model names to be be the table names unless stated otherwise in the model by.
protected $table = 'task';