How to join three tables with Laravel and keep data integrity - php

I am using Laravel 5.7 and now I am trying setup a relationship between three tables named:
Tickets (PK - TicketID, FK - CampusID)
Campus (PK - CampusID, FK - TechID)
User (PK - TechID)
I don't think I set up my models correctly as I am showing a ticket where the CampusID doesn't belong to the TechID. I am looking for a best practice on setting up Eloquent to keep the data integrity in place so I can prevent any abnormalities. As mentioned above the foreign key for Tickets should reference the Campus primary key, and Campus foreign key should reference the User primary key.
Here are my Models:
Ticket
protected $table='tickets';
public function user() {
return $this->belongsTo(User::class);
}
Campus
protected $table='campus';
public function user() {
return $this->belongsTo(User::class);
}
User
public function campus()
{
return $this->hasMany(Campus::class, 'TechID');
}
public function ticket()
{
return $this->hasMany(Ticket::class, 'AssignedTo');
}
Here is my controller:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
use App\Campus;
use App\Ticket;
class PagesController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
// Dashboard Page
public function index()
{
$user = Auth::user();
$campuses = Campus::where('TechID',$user->id)->pluck('CampusName');
$tickets = Ticket::all()->where('AssignedTo', $user->id);
return view('home')->with(['user' => $user,'campuses'=>$campuses,'tickets'=>$tickets]);
}
// Queue Page
public function Queue() {
return view('Pages.Queue');
}
// Reports Page
public function Reports() {
return view('Pages.Reports');
}
// Search Page
public function Search() {
return view('Pages.Search');
}
}
I think my models are fine, but my controller is probably where I made some mistakes. I've tried reading questions on here already, watching videos, and reading the Laravel docs, but nothing has really clicked with me yet. I really appreciate any and all help. Ideally it should cascade changes. So if I have a situation where I want to change what location a tech belongs to I could just make the change in the Campus table probably in the TechID column.

I would use Eager Loading.
public function index()
{
$user = User::with([
'campuses' => function($query) {
$query->select(['id', 'CampusName']);
},
'tickets'
])->where('id', Auth::id())->first();
$campuses = $user->campuses->pluck('CampusName');
$tickets = Ticket::all()->where('AssignedTo', $user->id);
return view('home')->with([
'user' => $user,
'campuses'=>$user->campuses->pluck('CampusName'),
'tickets'=>$user->tickets]);
}
EDIT
You need to update your User model.
public function campuses()
{
return $this->hasMany(Campus::class, 'TechID');
}
public function tickets()
{
return $this->hasMany(Ticket::class, 'AssignedTo');
}

Related

Cannot access Intermediate table in HasManyThrough relationship

My DB schema looks like this.
Now, in artisan tinker mode, When I try to query Details table from user Model, it shows me the records of the details table but I cannot access the the Cases Model for some reason, it always returns NULL in tinker.
This is my User Model
public function details()
{
return $this->hasManyThrough('App\Models\Detail', 'App\Models\Cases', 'user_id', 'case_id', 'id', 'id');
}
What am I doing wrong?
If for convenience you want to access Details directly from the User model then you can define relations as - (may seem like a little duplication but worth if it results in ease)
class User extends Model
{
public function cases()
{
return $this->hasMany(Case::class);
}
public function details()
{
return $this->hasManyThrough(Detail::class, Case::class);
}
}
class Case extends Model
{
public function details()
{
return $this->hasMany(Detail::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
class Detail extends Model
{
public function case()
{
return $this->belongsTo(Case::class);
}
}
Now both cases and details can be directly accessed via User record
$user->cases;
$user->details;
The idea of hasManyThrough is to skip the intermediate table. If you need to look at the cases and the details maybe you should define other relations for it.
// User model
public function cases()
{
return $this->hasMany(Cases::class, 'user_id');
}
// Cases model
public function details()
{
return $this->hasMany(Detail::class, 'user_id');
}
$users = User::with('cases.details')->get();
foreach ($users as $user) {
// an user
foreach ($user->cases as case) {
// a case
foreach ($case->details as $detail) {
// the details of a case
}
}
}

Laravel eloquent insert data with multiple relationship

I have 3 tables:
User
- id
- email
UserAccount
- id
- user_id
- account_id
Account
- id
- user_id
Verification
- id
- user_id
- guid
I am trying to achieve a post whenever I try to add a user, it will automatically add an account with empty fields but with user_id in it, Verification table with user_id also, at the same time once the Account has been created it should also record UserAccount user_id and account_id but I ended up this error using many to many relationship belongsToMany and sync. How do I add the acct_id and user_id with eloquent?
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'acct_id' cannot be null (SQL: insert into `user_accounts` (`acct_id`, `user_id`) values (?, 17))
This is what I've tried so far.
Controller.php
$user = new User();
$user->name = "Not set";
$user->email = $email;
$user->password = NULL;
$user->save();
$accounts = new Account();
$accounts->email = $email;
$user->account()->save($accounts);
$userAccount = new UserAccount();
$userAccount->userAccount()->sync([
$user->id,
$accounts->id
]);
User.php
public function account()
{
return $this->hasMany(Account::class);
}
public function userAccount()
{
return $this->belongsToMany(User::class, UserAccount::class, 'user_id', 'id');
}
UserACcount.php
public function user()
{
return $this->hasMany(User::class, 'user_id', 'id');
}
public function account()
{
return $this->hasMany(Account::class, 'acct_id', 'id');
}
public function userAccount()
{
return $this->belongsToMany(Account::class, UserAccount::class, 'acct_id', 'user_id');
}
Verification.php
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
Account.php
public function user()
{
return $this->hasMany(User::class);
}
public function userAccount()
{
return $this->belongsTo(UserAccount::class);
}
I tried using this functionality and completely works fine but pretty sure this is how it works with eloquent.
$userAcct = new UserAccount();
$userAcct->create([
'user_id' => $user->id,
'acct_id' => $accounts->id
]);
Any thoughts?
I also have did found this related problem (Laravel hasManyThrough)
First of all, you should remove user_id from the account table because it is already referenced by user_account which links both tables. Moreover, if you wanna take advantages of Eloquent conventions which allow it to guess table names and fields, you should make sure your tables are named users, accounts, verifications and account_user.
User.php
public function accounts()
{
return $this->belongsToMany(Account::class);
}
Account.php
public function users()
{
return $this->belongsToMany(User::class);
}
The UserAccount model is useless if account_user exists only to links 2 tables.
Then, you may use an observer to get an event-based approach: whenever an user is created => do something
https://laravel.com/docs/5.8/eloquent#observers
<?php
namespace App\Observers;
use App\Account;
use App\Verification;
class UserObserver
{
/**
* Handle the User "created" event.
*
* #param \App\User $user
* #return void
*/
public function created(User $user)
{
(new Verification)->user()->associate($user);
$account = new Account;
$account->save();
$user->accounts()->attach([
$account->id
]);
}
}
// Post Model
public function user()
{
return $this->belongsTo('App\User');
}
public function categories()
{
return $this->belongsToMany('App\Category')->withTimestamps();
}
public function tags()
{
return $this->belongsToMany('App\Tag')->withTimestamps();
}
//User Model
public function posts()
{
return $this->hasMany('App\Post');
}
//Category Model
public function posts()
{
return $this->belongsToMany('App\Post')->withTimestamps();
}
//Tag Model
public function posts()
{
return $this->belongsToMany('App\Post')->withTimestamps();
}

Laravel 5.1 eloquent query syntax

I have the following model relationships. If a user logs in as an employee, I want them to be able to get a list of employees for a their company and the roles they have been assigned:
class User {
// A user can be of an employee user type
public function employee()
{
return $this->hasOne('App\Employee');
}
//
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
class Employee {
// employee profile belong to a user
public function user()
{
return $this->belongsTo('App\User');
}
// employee belongs to a company
public function company()
{
return $this->belongsTo('App\Company');
}
}
class Company {
public function employees()
{
return $this->hasMany('App\Employee');
}
}
But the following query doesnt work. I get error Column not found: 1054 Unknown column companies.id in WHERE clause:
$employee = Auth::user()->employee;
$companyEmployees = Company::with(['employees.user.roles' => function ($query) use ($employee) {
$query->where('companies.id', '=', $employee->company_id)
->orderBy('users.created_at', 'desc');
}])->get();
The users and the employees table have a one to one relationship.
All employees have a base role type of employee in addition they may also have other roles such as manager, supervisor etc.
How do I write a query that gives me a company with all its employees and their roles?
I've tried to add a hasManyThrough relation to the Company model but that doesn't work either?
public function users()
{
return $this->hasManyThrough('App\User', 'App\Employee');
}
I think you're ring to get a list of coworkers for the current user and eager load the user and role?
$employee = Auth::user()->employee;
$companyEmployees = Company::with(['employees.user.roles')->find($employee->company_id);
Or perhaps:
$companyEmployees = Company::find($employee->company_id)->employees()->with('user.roles')->get();
$sorted = $companyEmployees->sortBy(function($employee){ return $employee->user->created_at; });
That might be a more direct route. Is your employee id in the user table or vice versa? The eloquent relationships are easy to set backwards.
Users::select('table_users.id')->with('roles')->join('table_employes', function($join) use ($employee) {
$join->on('table_employes.user_id','=','table_users.id')->where('table_employes.company_id', '=', $employee->company_id);
})->orderBy('tables_users.created_at')->get();
1. Create relationship for database table columns in migrtaion :
User Role
$table->foreign('user_id')->references('id')->on('users');
Users
$table->increments('id');
2. Create a model for each database table to define relationship
User.php (model)
public function userRoles()
{
return $this->hasOne('App\UserRoles', 'user_id', 'id');
}
Userroles.php (model)
public function user()
{
return $this->belongsTo('App\User', 'user_id', 'id');
}
3. Let controller handle database calls recommended to use REST api
Controller
use App\User;
use App\UserRoles;
class UserController extends Controller
{
public function index()
{
return User::with('userRoles')->orderBy('users.created_at', 'desc')->paginate(50);
}
}

`Trying to get property of non-object` using `hasManyThrough` relationship laravel 5

I am trying to develop a blog using Laravel 5 in which i have to show comment along with user on post.
Here is my database table schema.
User
id
name
Posts
id
post_content
user_id
Comments
id
comment
user_id
post_id
Here is my User Model
public function posts()
{
return $this->hasMany('App\Models\Posts');
}
public function comments(){
return $this->hasManyThrough('App\Models\Comments','App\Models\Posts');
}
Here is my Posts Model
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function comments(){
return $this->hasMany('Ap\Models\Comments');
}
Here is my Comment Model
public function posts()
{
return $this->belongsTo('App\Models\Posts');
}
public function user(){
return $this->posts->name;
}
Here is my code how i am accessing user name
$comments = Comments::find(1);
$comment['comment'] = $comments->comment;
$comment['user_name'] = $comments->name;
$comment['post_id'] = $comments->posts->id;
may be i am not getting in right direction? if i am doing right then why it is not working.
In laravel 5 you do not call the model as you are doing. since the models are stored in the app folder just call like. Plus I think you need to define the relationship
class User extends Model {
public function phone()
{
return $this->hasOne('App\Phone');
}
}
class Phone extends Model {
public function user()
{
return $this->belongsTo('App\User');
}
}
$phone = Phone::find(1);
For the case of foreign keys and more regarding the Eloquent relationships in laravel 5 just follow the documentation on the laravel website. Make sure to look at dynamic properties of that are allowed by eloquent
http://laravel.com/docs/5.0/eloquent

Distant HasManyThrough

I have four Models:
User
Client
Store
Opportunity
The relationships are defined as such:
User hasMany Client
Client hasMany Store
Store hasMany Opportunity
User hasManyThrough Store, Client (this works)
The problem is that I'm attempting to access the User->Opportunity relationship via built-in Laravel relationships, but it doesn't seem as if I can do it without a custom Query or an additional user_id column on the opportunities table to allow direct access (even though one can be inferred from the Store->Client relationship). I'm also not a fan of nested foreach loops if they can be avoided.
My question:
Is there a way to go one level deeper and directly access a User's Opportunities in this scenario? The actual Model code and all relevant relationships are as follows:
User
class User extends Eloquent{
public function clients(){
return $this->hasMany('Client');
}
public function stores(){
return $this->hasManyThrough('Store', 'Client');
}
public function proposals(){
return $this->hasMany('Proposal');
}
public function opportunities(){ //This does the job, but I feel like it could be better
return Opportunity::join('stores', 'stores.id', '=', 'opportunities.store_id')->
join('clients', 'clients.id', '=', 'stores.client_id')->
join('users', 'users.id', '=', 'clients.user_id')->
select('opportunities.*')->
where('users.id', $this->id);
}
public function getOpportunitiesAttribute(){ //This just helps mimic the hasManyThrough shorthand
return $this->opportunities()->get();
}
}
Client
class Client extends Eloquent{
public function stores(){
return $this->hasMany('Store');
}
public function user(){
return $this->belongsTo('User');
}
public function opportunities(){
return $this->hasManyThrough('Opportunity', 'Store');
}
}
Store
class Store extends Eloquent {
public function client(){
return $this->belongsTo('Client');
}
public function opportunities(){
return $this->hasMany('Opportunity');
}
}
Opportunity
class Opportunity extends Eloquent {
public function store(){
return $this->belongsTo('Store');
}
}
I don't think there is such method in Laravel. You have to create your custom query. This custom query can be very expensive since multiple queries will be performed. Thus, the optimum solution for this, according to me, is to relate User and Opportunity with a foreign key.
However, if you don't desire to link User and Opportunity with a foreign key, then you can create a custom query to handle this. Simply add a "hasManyThrough" relation between Opportunity and Client model like,
<?php
class Client extends Eloquent{
public function store(){
return $this->hasMany('Store');
}
public function user(){
return $this->belongsTo('User');
}
public function opportunity(){
return $this->hasManyThrough('Opportunity', 'Store');
}
}
Then create a static function in User model.
<?php
class User extends Eloquent implements UserInterface, RemindableInterface {
use UserTrait, RemindableTrait;
public function client(){
return $this->hasMany('Client');
}
public function store(){
return $this->hasManyThrough('Store', 'Client');
}
public static function getOpportunityOfUser($userId)
{
$clients = User::find($userId)->client;
foreach ($clients as $client) {
$opportunities[] = Client::find($client->id)->opportunity;
}
return $opportunities;
}
}
Now you can access Opportunity realted to a User in one go like,
Route::get('/', function()
{
return $usersOpportunities = User::getOpportunityOfUser(1);
});
This will return all opportunity of all clients related to User with id '1'.
I created a HasManyThrough relationship with unlimited levels: Repository on GitHub
After the installation, you can use it like this:
class User extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function opportunities() {
return $this->hasManyDeep(Opportunity::class, [Client::class, Store::class]);
}
}

Categories