So, I have a legacy Database, with a poorly designed structure that has recently been moved to Laravel, but with some hacky nonsense to get it to work with models. Given the following Tables:
|==========================|====|============|===========|
| companies | id | token | name |
|--------------------------|----|------------|-----------|
| people_{companies.token} | id | first_name | last_name |
|==========================|====|============|===========|
The companies table contains multiple records, with an auto-incrementing ID, unique token, and name.
Each Company has its own people_{companies.token} table, instead of a single people table, with an associated client_id.
At first, this meant I couldn't use a standard Company and Person Model/Relationships, as protected $table needs to be static. We got around this with a DynamicBinding Trait:
<?php
namespace App\Models\Traits;
trait DynamicBinding {
protected $connection = null;
protected $table = null;
public function bind(string $connection, string $table) {
$this->setConnection($connection);
$this->setTable($table);
}
public function newInstance($attributes = [], $exists = false) {
$model = parent::newInstance($attributes, $exists);
$model->setTable($this->table);
return $model;
}
}
This allows for setting a table on the fly:
$company = Company::first();
$people = (new Person())->setTable("people_{$company->token}");
$person = $people->first();
This works perfectly fine, returning the first record from the people_{$company->token} table, and facilitating most functionality required. Now, I'd like to make this work with Relationships. Given the following example:
// Person.php
public function company() {
return $this->belongsTo(Company::class);
}
$company = Company::first();
$people = (new Person())->setTable("people_{$company->token}");
$person = $people->first(); // No Issue
$peopleWithCompany = $people->with('company')->first(); // Cannot find table `people`
This returns the error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'database.people' doesn't exist (SQL: select * from people limit 1)
Essentially, as soon as ->with() (or other functions, like ->query(), etc) is appended, it tries to perform the query based on the determined table (people from Person) instead of the table set via $people->setTable().
Does anyone have any experience connecting models to this kind of data structure, while allowing use of Eager Loading with Relationships? And sidenote, there is a plan to migrate everything to a single people table, but unfortunately not anytime soon...
Thanks in advance!
Related
I'm using Laravel and I have a problem creating a team with a coach's id using eloquent expressions. I have users (basic users who can become coaches), then I have coaches who can have only one team and lastly teams, which can have 1 or 2 coaches. Their tables:
Users:
id | email | password | coach_id (null is default)
Coaches:
id | user_id | ... (other unnecessary coach info)
Teams:
id | coach_id | ... (other unnecessary team info)
I tried creating a team in TeamController's method (following laravel tutorial on Youtube https://youtu.be/z-1bdYTNWm8?t=6m50s):
$team = new Team();
$team->team_name = $team_name;
$team->organization = $organization;
$team->address = $address;
$request->user()->coach()->team()->save($team);
My user model:
public function coach(){
return $this->hasOne('App\Coach');
}
My coach model:
public function user(){
return $this->belongsTo('App\User');
}
public function team(){
return $this->hasOne('App\Team');
}
My team model:
public function coach(){
return $this->belongsToMany('App\Coach');
}
But i get this error:
BadMethodCallException in Builder.php line 2450:
Call to undefined method Illuminate\Database\Query\Builder::team()
You can try it as:
$team = new Team();
$team->team_name = $team_name;
$team->organization = $organization;
$team->address = $address;
$request->user()->coach->team()->save($team);
Note - Remove the () from coachrelation.
Update
When you add parenthesis () it creates the query builder and you don't have any relation named team in User model, so Laravel throws the error.
But when you do $request->user()->coach it returns the object of Coach model and then you can query it by the relation name team.
The relevant portion of my application is set up as follows:
A Users table, with unique user IDs
A Teams table, with unique team IDs
A Team_Membership table, with
a unique ID and a column for a User ID and a Team ID, to denote
that a user belongs to a team.
A user can be on an unlimited number of teams.
I have a function to retrieve an array of the teams a given user belongs to. It would generally be called in the scope of the current logged in user to get their active teams, but will also be used in administrative functions to return the teams of a given user ID.
My first thought was to make the function callable from the user model, so for a given user object, I could do as follows
$teams = $user->get_teams();
or
$teams = User::get_teams($user_id);
But I'm not sure if this is generally considered best practice in Laravel for this type of functionality. Where should this function be located?
Actually, you are talking about something that laravel already does for you if you are using Eloquent.
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $table = 'users';
public function teams()
{
return $this->belongsToMany(Team::class);
}
}
class Team extends Model
{
protected $table = 'teams';
public function users()
{
return $this->belongsToMany(User::class);
}
}
In addition to the users and teams table you would make a pivot table:
team_user
id - integer
team_id - integer
user_id - integer
and laravel does the rest.
$userId = 1;
$user = User::with('teams')->find($userId);
$teams = $user->teams;
I have two tables 1)users
{ id, password }
2)expertise { id, expertise}
the relationship I have is
Models
Expertise.php
function User()
{
$this->hasOne('Expertise');
}
User.php
function Expertise()
{
$this->hasOne('User');
}
So how can I query using Eloquent to get the first 10 users with a certain expertise?
I want to join users.id = expertise.id and get the first 10 people with a specified expertise (Where clause).
Beginner to laravel, I've checked other sources but was not successful
Right now you are having a problem with the way that you modeled your data. If you have a one-to-one relationship the best practice to model it is to have one entity store the id of the other. The Laravel convention for this is to have a column named <model>_id:
Users
| id | password |
Expertises
| id | expertise | user_id |
Then in your models you can do this:
Models
Expertise.php
class Expertise extends Eloquent
{
public function User()
{
// because expertise has a column user_id
// expertise belongs to user
return $this->belongsTo('User');
}
}
User.php
class User extends Eloquent
{
public function Expertise()
{
// because expertise is the one with the column
// user_id, user has one expertise
return $this->hasOne('Expertise');
}
}
The Query
After you have all this set up, to be able to query the first 10 users with a certain expertise you can do this.
$users = User::whereHas('Expertise', function($q)
{
$q->where('expertise', '=', <expertise you are looking for>)
})
->take(10)
->get();
To get a further reading in querying relationships in Laravel please take a look at this:
Laravel - Querying Relationships
Keep in mind
keep in mind that the tables name must be plural, if not then you should specify the name of the table inside the model:
protected $table = 'expertise';
I'm trying to get my head around laravel models & one to many...
I have the following tables
ww_bookings
-----------------------------
booking_id
customer_id
quote_id
ww_quotes
-----------------------------
quote_id
etc
etc
etc
I'm using sentry for my auth and basically on a sucsessful login I want to find the id of the logged in user and then query the ww_bookings data WHERE customer_id = 'logged in user id'.
Once it's good all the bookings for the customer_id it then need to go and query ww_quotes for each booking found and bring back the data.
//Controller
BookingData::find(1)->quotes()->where('quote_id', '=', '1')->get();
//BookingData Model
class BookingData extends Eloquent {
protected $table = 'ww_bookings';
protected $primaryKey = 'customer_id';
public function quotes() {
return $this->hasMany('QuoteData');
}
}
//QuoteData Model
class QuoteData extends Eloquent {
protected $table = 'ww_quotes';
}
I get the following error:
Column not found: 1054 Unknown column 'ww_quotes.booking_data_id' in 'where clause' (SQL: select * from ww_quotes where ww_quotes.booking_data_id = 1 and quote_id = 1)
Can anyone help me out, it's been driving me crazy...
Hope it makes sense..
There are two problems I see:
Wrong relationship
The problem is that ww_quotes schema/table should contain key that refers ww_booking schema/table - exactly in reverse.
Solution to this:
ww_bookings
-----------------------------
customer_id
quote_id
ww_quotes
-----------------------------
quote_id
booking_id
Key will not match
Key names that Eloquent generates and uses for relations will not match existing key names. If you specify them, Eloquent use them instead.
return $this->hasMany('QuoteData', 'booking_id');
When i use FuelPHP ORM to get ALL results using find('all'), it's return only one record.
This is my db.
table name ws_config. (no primary key)
--------------------------
config_name | config_value |
--------------------------
site_name | My Site |
--------------------------
member_allow_register | 1 |
--------------------------
This is my model.
class Model_Config extends Orm\Model
{
protected static $_table_name = 'config';
protected static $_primary_key = array();// no PK, need to set PK to empty array.
}
This is my controller
class Controller_Account_Register extends \Controller_Basecontroller
{
public function action_index()
{
$config = Model_Config::find('all');
$output['config'] = $config;
// call function below is in base controller. it is just load theme (this view page into main template) nothing special.
return $this->generatePage('front/templates/account/register_v', $output);
}
}
This is my view file.
foreach ($config as $row) {
//print_r($row);
echo $row->config_name;
echo ' = ';
echo $row->config_value;
echo '<br>';
}
The result is just
site_name = My Site
How to get ALL results from this database table? or
How to get multiple results upon where ondition?
The issue here is that indeed as #vee says the ORM expects you to have a primary key assigned to your table. By default in the orm this is simply a column called "id". If you do not specify a PK unexpected behaviour happens, such as this.
Once you define a primary key on your table this issue should be resolved.
The simplest thing would be to just add an auto-incrementing ID column as this is the default for orm models.
FuelPHP ORM needs Primary key to get ALL results.
No PK you can get only one result.
erm... :(