How to query another table inside a BuildRules() Function - CakePHP 4.x - php

I am trying to query a different table inside my SRC/Model/Table/InspectorsTable.php file within the buildRules() function.
Background info:
Currently, my system uses a User table to store login information. HERE is the model.
This Users table has 3 different profile tables associated with it. Inspectors, Contractors, and Employees.
As of now, an Inspector, Contractor, and Employee record can use the same User foreign key to login.
I am trying to make it so that a single User record, can only use a single record from either Inspector OR Contractor OR Employee to login.
E.g. 1 User Record for 1 Employee Record/Inspector Record/Contractor Record.
I was thinking to do that, I could use the buildRules() function to check the other tables to see if the primary key of Users table is already being used by another table on the creation of an Inspector, Contractor, or Employee. If it is, it will not create the record.
My Attempt:
To try and implement the above logic, I have tried to query one of my other tables (as seen below). However, this is not working.
Here is the SRC/Model/Table/InspectorTables.php:
class InspectorsTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('inspectors');
$this->setDisplayField('id');
$this->setPrimaryKey('id');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER',
]);
}
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add($rules->existsIn(['user_id'], 'Users'), ['errorField' => 'user_id']);
// this line does not work
print_r($this->Contractor->get(10000000));
return $rules;
}
}
However, I am getting this error when I try to do this:
Undefined property Contractor. You have not defined the Contractor
association on App\Model\Table\InspectorsTable
I believe this is occurring because of the association's setup in the initialize() function of the InspectorsTable.php
Any help/alternative solutions would be great! Thank you!

There is no association between Inspectors and Contractors, hence there's no magic association property that you could access on your InspectorsTable class.
To access "unrelated" tables, use the table locator, for example via the LocatorAwareTrait:
class InspectorsTable extends Table
{
use Cake\ORM\Locator\LocatorAwareTrait;
// ...
public function buildRules(RulesChecker $rules): RulesChecker
{
// ...
$contractorsTable = $this->getTableLocator()->get('Contractors');
return $rules;
}
}
Not sure if your schema is the best solution for your problem domain, so I'm not really going to comment on that, just a note that it looks somewhat unoptimized with all that duplicate fields.

Related

Laravel table relationship to pivot table

I'm confused on how to get my model setup in laravel with a table that is connected to a pivot table.
Here's the problem
Say I have
Locations
id
name
area_types
id
name
area_type_location (Pivot table)
id
location_id
area_type_id
area_test
id
area_type_location_id
clean
headCount
Relationship between the tables are different areas type belongs to different locations.
i.e: beach, 25m pools, kids pools, bbq, etc
area_test is connected to the pivot table because the test has to be generated from area that exists, in this case it is area that is registered under different locations. Thus it has to be tested daily, measured, etc.
I understand the structure between area_types and locations for many to many relationship, however I can't get over my head of how do i structure my area_test model? How do I get the data from locations table -> where are my test?
Should I create a model for my pivot table? Is that a good practice in laravel?
Does anyone has the same use case?
I read about eloquent has many through
relationship but I understand that it does not mention about getting through pivot table. I don't quite get if my use case is the same.
Thanks
Finally, apparently there are a couple of way to get data from locations table to area_tests
Tried at tinker and it works,
First Option
I need to create a Pivot model for my Pivot table:
class LocationAreaType extends Pivot{
public function location(){
return $this->belongsTo(Location::class);
}
public function areaType(){
return $this->belongsTo(AreaType::class);
}
public function AreaTests(){
return $this->hasMany(AreaTest::class, 'area_type_location_id');
}
}
I can use hasManyThrough relation that I need to create in my Location table
public function areaTests()
{
return $this->hasManyThrough(
AreaTest::class,
LocationAreaType::class,
'location_id',
'area_type_location_id');
}
this way I can get the areaTests easily by $location->areaTests, My problem was not determining the area_type_location_id as foreign. You need to determine this, apparently when I extends pivot and use hasMany laravel does not auto recognise the Foreign key by itself.
Second option
Another way to access it is from the relation table, I can define withPivot in the areaTypes() relation then access it like this:
$location->areaType[0]->pivot->areaTests
Since laravel only recognise foreign key from both tables location_id and area_type_id, I have to include the id of the pivot table to get the AreaTest table data
So in the Location model I have to get the column
public function areaTypes()
{
// Get the ID of the pivot table to get the poolTests table (connected with ID column)
return $this->belongsToMany(AreaType::class)
->using(AreaTypeLocation::class)
->withPivot('id');
}
There is no need to create a new model for pivot table.
Just declare in Location model below code:
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function area_types()
{
return $this->belongsToMany('App\AreaType', 'area_type_location', 'location_id', 'area_type_id');
}
and declare below code in AreaType model:
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function locations()
{
return $this->belongsToMany('App\Location', 'area_type_location', 'area_type_id', 'location_id');
}
every time you need to get for example the locations of an area_type in every controller, you can call the function like this: $areatype->locations()->get();
Don't forget to create area_type_location table migration.

How to create a correct relationship between Laravel models

So I have two tables named 'customers' and 'billing_addresses'.
I also have two models named 'Customer' and 'BillingAddress'.
In the billing_addresses table, there are multiple addresses, each having an 'id'.
In the customers table, each customer has an 'billing_address_id' value corresponding to one of the entries in the billing_addresses table.
What I managed to create so far is:
// Customer relationship method(in the model file)
public function address()
{
return $this->hasOne('App\BillingAddress','id');
}
/////////
// BillingAddress relationship method(in the model file)
public function customer()
{
return $this->hasMany('App\Customer','id');
}
I am indeed getting the right data when I do something like Customer::with('address');
but I feel like there is a better approach for all this as I'm also getting
BadMethodCallException
Call to undefined method
Illuminate\Database\Eloquent\Relations\HasOne::getForeignKey()
when I try to filter with datatables(can't tell if its related but first I'd want to have the best approach on the relationships).
Thank you!
What you want to achieve is this:
// Customer Model
public function address()
{
return $this->belongsTo('App\BillingAddress');
}
// BillingAddress Model
public function customer()
{
return $this->hasMany('App\Customer');
}
You need the belongsTo on your Customer Model because you have the Billing id stored within the Customer table. Eloquent will automatically match the billing id to the foreign Key in your customer model. You just need to follow Eloquente naming conventions for foreign keys. Look here for more infos.
Eloquent Relations
A custom can have multiple billing addresses. So it customer can have many addresses and an address belongs to a customer.
// Customer Model
public function addresses()
{
return $this->belongsTo('App\BillingAddress', 'customer_id');
}
// BillingAddress Model
public function customer()
{
return $this->hasMany('App\Customer', 'billing_address_id');
}
Then you can do : Customer::with('addresses')->get();
Secondly, make sure you are passing correct foreign key column as second parameter to the relationship methods. See documentation for reference
The second error is probably causing because of incorrect foreign key column passed as second argument.

Laravel 5 Relations

Database Structure:
-Users Table
-user_id
-name
-...
-Follow Table
-user_id
-follow_id
so when user follow another it will be inserted in follow table
and when get user followers
$user = User::where('user_id',$id)->first();
$user['followers'] = $user->Followers;
$user['following'] = $user->Following;
return $user;
By This Relation in User Model Side
public function Followers()
{
return $this->hasMany('App\Follow','follow_id','user_id');
}
And By This Relation in Follow Model Side
public function getUserData()
{
return $this->belongsTo('App\User','user_id','user_id');
}
It Works Fine for me and it gaves me every id But the problem is
that i want to get information about every user returned from this relation
so i should call User Model for each user returned to get his information
or what ??
The way you have you many-to-many relationship set up is almost right.
Firstly, change the Followers() method to be followers() as Laravel follows the PSR-2 standard.
Secondly, this isn't essential but change the user_id column in the users table to be just id. This is a laravel convension that does not need to be followed, however, at this point I don't see any reason not to follow it. I am assuming that you have something like:
protected $primaryKey = 'user_id';
in your User model. If you change your user_id column to id you won't have to have the above declaration anymore.
(If you don't have that line in and you want to continue using user_id as the primary key you WILL have to add that line to your User model.
Thirdly, change the relationship type in followers() to be:
public function followers()
{
return $this->belongsToMany('App\User', 'follower', 'user_id', 'follow_id');
//follower is the table name, user_id is column that relates to the current model and follow_id is the column that is for the relationships
}
With all of the above done you can now get a user with all of their followers by doing:
$user = User::with('followers')->find($id);
This will allow you to get the followers by simply doing:
$user->followers
At this point you can not get rid of you Follow model as you generally wont need a model for a pivot table.
To get the following relationship just add:
public function following()
{
return $this->belongsToMany('App\User', 'follower', 'follow_id', 'user');
}
to your User model.
Again to access this you can either:
$user = User::with('following')->find($id);
or if you have already have the user model and the relationship isn't loaded you can:
$user->load('following'); //in this case following is the name of the method where you have declared the relationship.
For more information please refer to the documentation http://laravel.com/docs/5.1/eloquent-relationships#many-to-many and http://laravel.com/docs/5.1/eloquent
Hope this helps!

Laravel 4 - Many-to-Many with timestamps OR Many-to-One with history

I have a bit of a mind boggling question.
The scenario:
I have an Order that needs to move from Process to Process. One order can only be in One Process at a time, but One Process can have Many Orders in it at any given time.
But then I also need a history of when the Order landed in which Process.
Somehow I can't get my mind around this, so if anyone can give me an idea of which direction you'd trot in, I would appreciate it very much.
I did notice when I created Departments for my Users, in my department_user table where I added the timestamps(), it doesn't actually update those timestamps when a new relationship is created between a user and a department. Is there any way to get that working? 'cause I'm thinking these two issues are very much related.
You might set up your orders table to have a column process_id and then set up the relationship as:
class Order extends Eloquent
{
public function process()
{
return $this->belongsTo('Process');
}
}
class Process extends Eloquent
{
public function orders()
{
return $this->hasMany('Order');
}
}
Regarding the order/process history, I would write that to a separate history table when moving the Order to a new Process.
class OrderProcessHistory extends Eloquent
{
protected $fillable = array(
'order_id',
'process_id'
);
protected $timestamps = true;
}
Then just something like...
OrderProcessHistory::create(array('order_id' => $order->id, 'process_id' => $process->id));

Yii - Belongs_To was working, now throws error

I have a User and Account model. The relationship is User belongs to Account, Account has many Users.
Here is the model code for both:
User Model:
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'account' => array(self::BELONGS_TO, 'Account', 'account_id'),
);
}
Account Model:
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'users' => array(self::HAS_MANY, 'User', 'account_id'),
'userCount'=>array(self::STAT,'User','account_id'),
);
}
I have this code in my UserIdentity.php for logging in which WAS working just fine:
public function authenticate()
{
$user=User::model()->findByAttributes(array('username'=>$this->username));
if($user===null)
$this->errorCode=self::ERROR_USERNAME_INVALID;
else{
if($user->password!==$user->encrypt($this->password))
$this->errorCode=self::ERROR_PASSWORD_INVALID;
else{
$this->_id=$user->id;
if($user->last_login_time==null)
$lastLogin=time();
else
$lastLogin=strtotime($user->last_login_time);
$this->setState('lastLoginTime', $lastLogin);
$this->setState('account',array('id'=>$user->account->id,'name'=>$user->account->name,));
$this->errorCode=self::ERROR_NONE;
}
}
return !$this->errorCode;
}
It started giving an error when I added another user to the account:
PHP Notice: Trying to get property of non-object.
The error points to
$this->setState('account',array('id'=>$user->account->id,'name'=>$user->account->name,));
When broken up into multiple lines:
'id'=>$user->account->id, is where the error lies.
To fix this, I simply changed it to this:
$account=Account::model()->findByPk($user->account_id);
$this->setState('account',array('id'=>$account->id,'name'=>$account->name,));
So the relationship worked just fine when I had a single user, but when I had 2 users, the relationship fails. I can continue using Yii as above, but I did like the simplicity of accessing the object directly. Did I not set up the relationships correctly? Why is this not working now with 2 users in one account?
EDIT:
var_dump($user) - http://pastebin.com/TEyrFnme
Also interesting is that I can access the user from the account using: $users=$account->users; and access all the $user[0] attributes just fine. So in reverse, the relationship seems to be working, just going forward seems to have difficulty.
Don't declare a variable in your model with the same name as a relation.
public $account;
will prevent the model from looking for the account relation, as Yii will first look for (and use) actual attributes before checking for relations of the same name.

Categories