I hope I can explain this clearly, apologies in advance if it is confusing. I have a goals table which hasOne of each of bodyGoalDescs, strengthGoalDescs and distanceGoalDescs as shown below
goals.php
class Goal extends BaseModel
{
protected $guarded = array();
public static $rules = array();
//define relationships
public function user()
{
return $this->belongsTo('User', 'id', 'userId');
}
public function goalStatus()
{
return $this->hasOne('GoalStatus', 'id', 'goalStatus');
}
public function bodyGoalDesc()
{
return $this->hasOne('BodyGoalDesc', 'id', 'bodyGoalId');
}
public function distanceGoalDesc()
{
return $this->hasOne('DistanceGoalDesc', 'id', 'distanceGoalId');
}
public function strengthGoalDesc()
{
return $this->hasOne('StrengthGoalDesc', 'id', 'strengthGoalId');
}
//goal specific functions
public static function yourGoals()
{
return static::where('userId', '=', Auth::user()->id)->paginate();
}
}
each of the three tables looks like this with the function details changed
class BodyGoalDesc extends BaseModel
{
protected $guarded = array();
public static $rules = array();
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'bodyGoalDescs';
//define relationships
public function goal()
{
return $this->belongsTo('Goal', 'bodyGoalId', 'id');
}
}
a goal has either a body goal, a strength goal, or a distance goal. I am having a problem with this method in the controller function
<?php
class GoalsController extends BaseController
{
protected $goal;
public function __construct(Goal $goal)
{
$this->goal = $goal;
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
$thisgoal = $this->goal->find($id);
foreach ($this->goal->with('distanceGoalDesc')->get() as $distancegoaldesc) {
dd($distancegoaldesc->DistanceGoalDesc);
}
}
}
when I pass through goal 1 which has a distance goal the above method dies and dumps the Goal object with the details of goal 1 and an array of its relations including an object with DistanceGoalDes.
when I pass through goal 2 it passes through exactly the same as if I had passed through goal 1
if I dd() $thisgoal i get the goal that was passed through
what I want ultimately is a method that returns the goal object with its relevant goal description object to the view but this wont even show me the correct goal details not too mind with the correct relations
this function is now doing what I want it to do, I am sure there is a better way (besides the fact that its happening in the controller right now) and I would love to hear it.
public function show($id)
{
$thisgoal = $this->goal->find($id);
if (!$thisgoal->bodyGoalDesc == null) {
$goaldesc = $thisgoal->bodyGoalDesc;
return View::make('goals.show')
->with('goal', $thisgoal)
->with('bodygoaldesc', $goaldesc);
} elseif (!$thisgoal->strengthGoalDesc == null) {
$goaldesc = $thisgoal->strengthGoalDesc;
return View::make('goals.show')
->with('goal', $thisgoal)
->with('strengthgoaldesc', $goaldesc);
} elseif (!$thisgoal->distanceGoalDesc == null) {
$goaldesc = $thisgoal->distanceGoalDesc;
return View::make('goals.show')
->with('goal', $thisgoal)
->with('distancegoaldesc', $goaldesc);
}
}
Related
Context: Trying to extend Laravel models from a database table. Table models linked to App\Models\BootableModel, App\Models\User extends this class.
I have the following code:
<?php
class BootableModel extends Model
{
protected $table = 'models';
protected $fillable = [
'name',
'class',
'table',
];
public function __construct(array $attributes = [])
{
$this->bootFromDatabase();
parent::__construct($attributes);
}
private function bootFromDatabase()
{
$class = static::class;
$bModClass = self::class;
Log::debug($class);
Log::debug($bModClass);
//$bootableModel = DB::table('models')->where('class', $class)->first();
$bootableModel = $bModClass::where('class', $class)->first();
if(!$bootableModel) {
return;
}
Log::debug($bootableModel->id);
The debug of $bModClass shows App\Models\BootableModel as expected (self vs static), but for some reason the $bModClass::where is trying to query the users table. Using a direct reference to App\Models\BootableModel::class does not change this, so it's not self::class that is the issue. The debug output as proof:
[2021-02-21 17:33:39] local.DEBUG: App\Models\User
[2021-02-21 17:33:39] local.DEBUG: App\Models\BootableModel
It should never try to access User::where(), and as such, it should never try to use User::$table either, but somehow it does.
Is Laravel doing some weird reflection, or is this normal PHP behavior? Is there a way around this?
Update:
I have found a workaround, but I'm not satisfied with this being the correct/only solution:
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
static::bootFromDatabase();
}
public static function bootFromDatabase()
{
$class = static::class;
$bModClass = self::class;
if($class === $bModClass) {
return;
}
Have you ever tried self::where(...) instead of $bModClass::where(...) ?
Similar situation:
class Base {
public static function where()
{
return 'where from base';
}
public static function getName()
{
return self::where();
}
}
class User extends Base {
public static function where()
{
return 'where from user';
}
}
echo User::getName();
Output: where from base
I am building a REST API on Laravel. I currently have a Model ( Project ) which has 2 Dynamic Methods ( liveindexes and totalindexes ).
I want the API (/projects) to return the values of both those methods as well.
Project Controller
public function index()
{
return Project::paginate();
}
Project Model
class Project extends Model
{
protected $table = 'projects';
protected $fillable = [
'target'
];
public function indexes()
{
return $this->hasMany('App\indexes','project_id','id');
}
public function totalindexes()
{
return $this->indexes()->count();
}
public function liveindexes()
{
return $this->indexes()->whereNotNull('anchor')->count();
}
}
To include these items with your Project model, add them to your model's attributes, attributes are appended when to model is transformed into an array, or it's JSON form.
Add this to your code:
protected $appends = ['live_indexes', 'total_indexes'];
public function getLiveIndexesAttribute() {
return $this->attributes['live_indexes'] = $this->liveIndexes();
}
public function getTotalIndexes() {
return $this->attributes['total_indexes'] = $this->totalindexes();
}
I'm working with a large json that returns several data from the database and I need to return an integer model that does not have any kind of relationship, I just need to return all the records that LampModels model with this great json. But Laravel always returns me Illegal offset type.
Controller
public function showAllUdiJson()
{
$allLamps = LampModels::all();
return Ilumination::with('street')
->with('neighborhood')
->with('iluminationinfo')
->with('economyplan')
->with('lamp')
->with('reactor')
->with('aluminumcable')
->with('steelconduit')
->with('alllamps', $allLamps)
->with('ticket')->get();
}
LampModels
<?php
class LampModels extends \Eloquent {
protected $fillable = [];
protected $table = 'lampmodel';
}
Illumination
<?php
class Ilumination extends \Eloquent {
protected $fillable = [];
protected $table = 'ilumination';
public function street()
{
return $this->belongsTo('street');
}
public function neighborhood()
{
return $this->hasOne('neighborhood', 'id');
}
public function iluminationinfo()
{
return $this->hasOne('iluminationinfo');
}
public function ticket()
{
return $this->hasMany('ticket');
}
public function economyplan()
{
return $this->hasOne('economyplan', 'id' ,'street_id');
}
public function lamp()
{
return $this->hasOne('lamp', 'id');
}
public function reactor()
{
return $this->hasOne('reactor', 'id');
}
public function aluminumcable()
{
return $this->hasOne('aluminumcable', 'id');
}
public function steelconduit()
{
return $this->hasOne('steelconduit', 'id');
}
}
See the error
Your error report is quite bad, but seems that your Ilumination model doesnt have an alllamps method.
You should attacth LampModels to your Ilumination model with a relationship, insted of doing what you doing, cause is a wrong approach.
I think you access somewhere ticket method which was created in Illumination Model that offset error encountered..
public function ticket()
{
return $this->hasMany('ticket');
}
if you want to access illumination->ticket, you must use this method with loop.
foreach(illumination->tickets as ticket) {
$field1 = ticket->field1;
}
If your still facing any issue than share your error log page here..
Is there any way to get a model by table name?
For example, I have a "User" model, its table is defined as protected $table = "users"
Now, what I want to do is to get the model by table name which is equal to "users".
This function is more like the reverse of Model::getTable();
I have searched everywhere but I could not find a solution, perhaps I might be missing something simple?
EDIT
I am building something like an API :
Route::get('/{table}', 'ApiController#api');
Route::get('/{table}/filter', 'ApiController#filter');
Route::get('/{table}/sort', 'ApiController#sort');
Route::get('/{table}/search', 'ApiController#search');
so in the address bar, for example when I search for the "users", I could just hit on the URL:
api/users/search?id=1
then on the controller, something like:
public function search(){
// get all the params
// get the model function
$model = //function to get model by table name
// do some filtering, then return the model
return $model;
}
Maybe something like this will help you:
$className = 'App\\' . studly_case(str_singular($tableName));
if(class_exists($className)) {
$model = new $className;
}
studly_case() and str_singular() are deprecated functions.
You can use the Illuminate\Support\Str facade.
$className = 'App\\' . Str::studly(Str::singular($tableName));
I know that it is an old question, but it can help someone:
public function getModelFromTable($table)
{
foreach( get_declared_classes() as $class ) {
if( is_subclass_of( $class, 'Illuminate\Database\Eloquent\Model' ) ) {
$model = new $class;
if ($model->getTable() === $table)
return $class;
}
}
return false;
}
It will return the class name, so you need to instantiate it.
You must determine for which table name which class to call.
I see 2 ways to do this.
Use Laravel's models naming convention as #IgorRynkovoy suggested
or
Use some kind of dictionary
public function search($tableName)
{
$dictionary = [
'table_name' => 'CLASS_NAME_WITH_NAMESPACE',
'another_table_name' => 'CLASS_NAME_WITH_NAMESPACE',
];
$className = $dictionary[$tableName];
$models = null;
if(class_exists($className)) {
$models = $className::all();
}
// do some filtering, then return the model
return $models;
}
Alternative variant.
I have my base model App\Models\Model
This model have static method getModelByTable, ofcourse you can store this method anywhere you want.
public static function getModelByTable($table)
{
if (!$table) return false;
$model = false;
switch ($table) {
case 'faq':
$model = Faq::class;
break;
case 'faq_items':
$model = FaqItems::class;
break;
}
if ($model) {
try {
$model = app()->make($model);
} catch (\Exception $e) {
}
}
return $model;
}
Inherit from the following, instead of from Model.
use Illuminate\Support\Str;
class EnhancedModel extends \Illuminate\Database\Eloquent\Model
{
/**
* The table associated with the model. Copies $table in Model
*
* #var string
*/
protected static string $tableName;
/**
* Get the table associated with the model. Copies getTable() in Model
*
* #return string
*/
public static function getTableName(): string
{
return static::$tableName ?? Str::snake(Str::pluralStudly(class_basename(static::class)));
}
/**
* Get the table associated with the model. Overrides getTable() in Model
*
* #return string
*/
public function getTable(): string
{
return $this::getTableName();
}
}
To override the auto-guessed table name, add this to your EnhancedModel descendent class:
protected static string $tableName = 'the_table_name';
Looks Laravel 6 make some changes. The following works fine for me
use Illuminate\Support\Str;
....
$className = 'App\\' . Str::studly(str::singular($table_name));
if(class_exists($className)) {
$model = new $className;
}
For some reason, I cannot chain model objects. I'm trying to eager load 'Location' for an 'Order' and would prefer the logic to be contained in the models themselves. But past one chain, it does not work.
class Order extends Eloquent {
protected $table = 'orders';
public function customer() {
return $this->belongsTo('Customer');
public function location() {
return $this->customer()->location(); // this does not work
}
}
class Customer extends Eloquent {
protected $table = 'customers';
public function user() {
return $this->belongsTo('User');
}
public function orders() {
return $this->hasMany('Order');
}
public function location() {
return $this->user()->location();
// return $this->user(); // WORKS!!
}
}
class User extends Eloquent {
protected $table = 'users';
public function locations() {
return $this->hasMany('Location');
}
public function location() {
return $this->locations()->first();
}
}
I eventually want to do this:
class ChefController extends BaseController {
public function get_orders() {
$chef = $this->get_user_chef(); // this already works
return $chef->orders()->with('location')->get(); // does not work
}
}
Try to reference relation (user table) by adding user_id as second argument, like this:
public function user() {
return $this->belongsTo('User',"user_id");
}
Maybe you called that id field different, but you know what I mean.