Invalid object name with Many to Many Relationship with 2 DB Connections - php

I am building a training web application to track associate training and certifications. I have created an Associate model that used a secondary DB connection to another database that has its information generated by another application so I have no control over the structure of the data. The associates table uses the associate's number as primary key and not an auto incremented ID. I have created a table to keep track of every training/certification course that take place. I created a many to many relationship between the Associate and the Course but when trying to add a record to the pivot table I am running an error.
"SQLSTATE[42S02]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Invalid object name 'associate_course'. (SQL: insert into [associate_course] ([associate_id], [course_id], [created_at], [updated_at]) values (0000, 1, 2020-01-31 18:36:56.390, 2020-01-31 18:36:56.390))",
Here is the function that is called to create a record in the pivot table (where the error occurs)
public function trained(Course $course, Request $request) {
$request->validate([
'associates' => 'required'
]);
$associates = explode(',', $request->associates);
foreach($associates as $associate_number) {
$associate = Associate::where('NUMBER', $associate_number)->first();
$course->associates()->attach($associate);
}
}
Here is my Associate model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Associate extends Model
{
protected $connection = 'SHOPFLOOR';
protected $table = 'USERS';
public function getRouteKeyName()
{
return 'NUMBER';
}
public function courses()
{
return $this->belongsToMany(Course::class, 'associate_course', 'associate_id', 'course_id', 'NUMBER', 'id')->withTimestamps();
}
}
and here is my Course model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Course extends Model
{
protected $fillable = ['course_type_id', 'name', 'date', 'expires', 'expires_at', 'notification_threshold'];
public function type() {
return $this->belongsTo(CourseType::class);
}
public function associates() {
return $this->belongsToMany(Associate::class, 'associate_course', 'course_id', 'associate_id', 'id', 'NUMBER')->withTimestamps();
}
}
I tried copying the sql from the error and running it on the database and it does insert the row into the database so that leads me to believe it's something with my Laravel configuration?
Can I get some assistance in fixing this issue?

It's hard to say without seeing your DB structure, Error says clearly that table specified in query is not found. Most probably is that you forgot to specify schema in the statement. IMHO code that you showed is not really related to the problem.

There is a bug with the Laravel sqlsrv driver. I needed to specify the full schema to get it working. In my case this was TRAINING.dbo.associate_course.
So relationships look like this
Associate.php
public function courses()
{
return $this->belongsToMany(Course::class, 'TRAINING.dbo.associate_course', 'associate_id', 'course_id', 'NUMBER', 'id')->withTimestamps();
}
Course.php
public function associates() {
return $this->belongsToMany(Associate::class, 'TRAINING.dbo.associate_course', 'course_id', 'associate_id', 'id', 'NUMBER')->withTimestamps();
}

Related

How do you define a 1-1 relationship bidirectionally with phalcon?

I have two tables, work_order and project. On the project records, there is a work_order_id field. There is no project_id on the work_order records. Do I need to add one?
Or is there a way to define these relationships using hasOne/belongsTo?
I've tried:
class WorkOrder extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasOne('id', Project::class, 'work_order_id');
}
}
class Project extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasOne('work_order_id', WorkOrder::class, 'id');
}
}
I can retrieve the WorkOrder from the project like so: $project->workOrder, but I cannot retrieve a Project from a WorkOrder using $workOrder->project. I want a bidirectional relationship.
How do I do this?
Try adding the alias parameter, since the implicit retrieval might try to use the class name and it wouldn't support namespaces in your models.
I found it quite bogus in phalcon 1/2/3 to work with hasOne. I've been using belongsTo since then until I re-wrote the pre-post-save part of the phalcon relationship manager for my personal needs. Keep in mind that belongsTo will be saved before the main model you are working with, other types of relationships will be created/updated after the main record is saved. I choose to use "belongsTo" or "hasOne" depending on the order that I want the records and their relationships to be saved.
class WorkOrder extends \Phalcon\Mvc\Model {
public function initialize() {
$this->belongsTo('project_id', Project::class, 'id', ['alias' => 'Project']);
}
}
class Project extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasOne('id', WorkOrder::class, 'project_id', ['alias' => 'WorkOrder']);
$this->hasMany('id', WorkOrder::class, 'project_id', ['alias' => 'WorkOrderList']);
}
}
Implicit retrieval should start with a ucfirst camelized string of your class name, or using a get.
$workOrder = WorkOrder::findFirst();
$project = $workOrder->Project;
$project = $workOrder->getProject(['deleted <> 1']);
$workOrderList = $project->WorkOrderList;
$workOrder = $project->WorkOrder;
$workOrder = $project->getWorkOrder(['deleted <> 1', 'order' => 'projectId desc']);

Laravel 5 – get particular many to many relation based on model and related model ID

I've got Tag and Attendee Eloquent models, they are in many-to-many relation. Pivot table has also two more attributes – value_int and value_string. My Attendee model looks like this:
class Attendee extends Model
{
public $timestamps = false;
protected $fillable = [
'event_id'
];
public function tags() {
return $this->belongsToMany('App\Models\Tag', 'attendee_tag', 'attendee_id', 'tag_id')
->withPivot(['value_string', 'value_int']);
}
public function scoreTagValue($tag_id) {
return $this->tags->where('tag_id', '=', $tag_id)->first();
}
}
What I want is to obtain pivot values based on Attendee model and variable tag_id, so I've written scoreTagValue function, but it always returns null and I don't know why :( I'm calling it this way:
$attendee->scoreTagValue($tag_id). Thanks for your help :)
You need to access the relation, not the property:
public function scoreTagValue($tag_id) {
return $this->tags()->where('tag_id', '=', $tag_id)->first();
}
Also, according to the docs, withPivot() does not take an array, so:
->withPivot('value_string', 'value_int');

Laravel Model renaming MySQL table to plural on query

I am using a model to query a remote MySQL DB and when I run the query, Laravel is trying to connect to the plural version of the table that I need it to connect to. The table's name is activity and the error I get is:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'health.activities' doesn't exist (SQL: select * from `activities`)
I just built the model for this using artisan make:model Activity so I am not sure what is going on. Here is my model:
<?php
namespace App;
use DB;
use Role;
use Illuminate\Database\Eloquent\Model;
class Activity extends Model
{
private $activity;
function __construct()
{
$this->activity = DB::connection('mysql_remote')->table('activity');
}
}
Here is my controller:
public function getDashboard()
{
$data = [
'page_title' => 'Dashboard',
'users' => User::getUser(),
'test' => Activity::get(),
];
return view('dashboard.dashboard', $data);
}
Anyone have any idea why this is happening?
Models expect the table to be named the plural of the name of the Model, in this case the plural of Activitiy is activities which is the table name it expects. If it's different, you need to add a table property to set the name of the table.
In your model, add the following...
protected $table = 'activity';

Laravel-5 and Multitenancy database setup

I'm starting to develop an SaaS application and I have created my database structure. I'm planning to create a middleware file which handles the database connection for that request. Within this middleware file I want to create a model which will always select only rows from any table that corresponds to the current connection cust_id (foreign key).
For example:
$Customers->where('cust_id', $cust_id)->first();
How can I do this without having to specify where('cust_id', $cust_id) in every select statement?
You can easily achieve that using Eloquent's global query scopes in your models. You can read more about them here: http://laravel.com/docs/5.1/eloquent#query-scopes
First, you need to define the Multitenant scope class, that will update all the queries that run and add the constraint on cust_id field:
class MultitenantScope implements ScopeInterface
{
public function apply(Builder $builder, Model $model)
{
if (Auth::id()) {
$builder->whereCustId(Auth::id());
} else {
$model = $builder->getModel();
// apply a constraint that will never be true
// so that no records are fetched for unauthorized users
$builder->whereNull($model->getKeyName());
}
}
public function remove(Builder $builder, Model $model)
{
$query = $builder->getQuery();
$query->wheres = collect($query->wheres)->reject(function ($where) {
return ($where['column'] == 'cust_id');
})->values()->all();
}
}
Then you need a trait that you will add to the models that need to be filtered:
trait MultitenantTrait
{
public static function bootMultitenantTrait()
{
static::addGlobalScope(new MultitenantScope());
}
public static function allTenants()
{
return (new static())->newQueryWithoutScope(new MultitenantScope());
}
}
The last piece is adding the MultitenantTrait to your model:
class SomeModel extends Eloquent {
use MultitenantTrait;
}
Now, every time you do any query using Eloquent's model methods, the cust_id constraint will be applied to the query and only models that belong to given cust_id will be available.
If for some reason you'll need to access all objects, you can use allTenants() method to run the query without the additional constraint:
$allRows = SomeModel::allTenants()->get();
Please keep in mind that I haven't tested that exact code, so let me know if you see any issues and I'll be more than happy to get that working for you :)

getting the value of an extra pivot table column laravel

I have a phone_models, phone_problems, and a phone_model_phone_problem pivot table. The pivot table has an extra column 'price'.
PhoneModel:
class PhoneModel extends \Eloquent
{
public function problems()
{
return $this->belongsToMany('RL\Phones\Entities\PhoneProblem')->withPivot('price');
}
}
PhoneProblem:
class PhoneProblem extends \Eloquent
{
public function models()
{
return $this->belongsToMany('PhoneModel')->withPivot('price');
}
}
What I'm trying to do is get the price of a specific phone with a specific problem.
This is how I have it now but I feel like Laravel has a built in Eloquent feature I can't find to do this in a much simpler way:
$model = $this->phoneService->getModelFromSlug($model_slug);
$problem = $this->phoneService->getProblemFromSlug($problem_slug);
all this does is select the specific model and problem from their slug.
then what I do is with those credentials I get the price like so:
$row = DB::table('phone_model_phone_problem')
->where('phone_model_id', '=', $model->id)
->where('phone_problem', '=', $problem->id)
->first();
so now I can get the price like so $row->price but I feel like there needs to be a much easier and more 'Laravel' way to do this.
When using Many to Many relationships with Eloquent, the resulting model automatically gets a pivot attribute assigned. Through that attribute you're able to access pivot table columns.
Although by default there are only the keys in the pivot object. To get your columns in there too, you need to specify them when defining the relationship:
return $this->belongsToMany('Role')->withPivot('foo', 'bar');
Official Docs
If you need more help the task of configuring the relationships with Eloquent, let me know.
Edit
To query the price do this
$model->problems()->where('phone_problem', $problem->id)->first()->pivot->price
To get data from pivot table:
$price = $model->problems()->findOrFail($problem->id, ['phone_problem'])->pivot->price;
Or if you have many records with different price:
$price = $model->problems()->where('phone_problem', $problem->id)->firstOrFail()->pivot->price;
In addition.
To update data in the pivot you can go NEW WAY:
$model->problems()->sync([$problemId => [ 'price' => $newPrice] ], false);
Where the 2nd param is set to false meaning that you don't detach all the other related models.
Or, go old way
$model->problems()->updateExistingPivot($problemId, ['price' => $newPrice]);
And remind you:
To delete:
$model->problems()->detach($problemId);
To create new:
$model->problems()->attach($problemId, ['price' => 22]);
It has been tested and proved working in Laravel 5.1 Read more.
Laravel 5.8~
If you want to make a custom pivot model, you can do this:
Account.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Account extends Model
{
public function users()
{
return $this->belongsToMany(User::class)
->using(AccountUserPivot::class)
->withPivot(
'status',
'status_updated_at',
'status_updated_by',
'role'
);
}
}
AccountUserPivot.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class AccountUserPivot extends Pivot
{
protected $appends = [
'status_updated_by_nice',
];
public function getStatusUpdatedByNiceAttribute()
{
$user = User::find($this->status_updated_by);
if (!$user) return 'n/a';
return $user->name;
}
}
In the above example, Account is your normal model, and you have $account->users which has the account_user join table with standard columns account_id and user_id.
If you make a custom pivot model, you can add attributes and mutators onto the relationship's columns. In the above example, once you make the AccountUserPivot model, you instruct your Account model to use it via ->using(AccountUserPivot::class).
Then you can access everything shown in the other answers here, but you can also access the example attribute via $account->user[0]->pivot->status_updated_by_nice (assuming that status_updated_by is a foreign key to an ID in the users table).
For more docs, see https://laravel.com/docs/5.8/eloquent-relationships (and I recommend press CTRL+F and search for "pivot")

Categories