Active record custom relation (not based on FK) - activerecord

I have 2 tables: production and *production_summary*. production store data about each production that is produced, sold or discarded. *production_summary* store daily summary about production. This is the relevant DB structure:
CREATE TABLE `production` (
`id` int(10) unsigned NOT NULL auto_increment,
`created` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `production_summary` (
`id` int(10) unsigned NOT NULL auto_increment,
`date` date NOT NULL,
`summary` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Models are generated by Gii and names respectively are: Production and ProductionSummary.
I defined relation like this:
class Production extends CActiveRecord
{
...
public function relations()
{
return array(
'summary' => array(self::HAS_ONE, 'ProductionSummary', '', 'on' => 'summary.date = t.created' ),
);
}
...
}
It's working fine when I get model:
$model = Production::model()->with( 'summary' )->findByPk($id);
But lazy loading fail with: unknown column t.created. And I get it: Yii doesn't join tables while lazy loading, just querying the ProductionSummary table.
Using with() is not a problem in my case, but start to wonder what is the right way of defining this type of relations?

As of Yii 1.1.9 you can define relations this way:
return array(
'summary' => array(self::HAS_ONE, 'ProductionSummary', array('date' => 'created')),
);

Related

Laravel Polymorphic Relationship - Many to Many issue

Have students, that can have documents.
Documents can either be 'just documents' that belong to students (normal one to many relationship)
However, Students can also have 'passports' and 'visas' (amongst others). Each passport and visa can have a document too. A single document can belong to many things (eg, one document can be associated with a passport and a visa). For the purpose of this troubleshooting, lets keep it simple and between Student / Passport (I've also left out other class stuff like fillable just to keep this brief).
Student Model:
class Student extends Model
{
public function documents() {
return $this->hasMany('App\StudentDocument');
}
public function visas() {
return $this->hasMany('App\StudentVisa');
}
public function passports() {
return $this->hasMany('App\StudentPassport');
}
}
Student Passport Class
class StudentPassport extends Model
{
public function student_documents()
{
return $this->morphToMany(StudentDocument::class, 'student_documentable');
}
}
Student Passport Store:
public function store(StudentPassportRequest $request, $student_id)
{
$student = Student::findOrFail($student_id);
$passport = $student->passports()->create($request->all());
if ($request->file('student_document_file')->isValid()) {
$uploaded_file = $request->file('student_document_file');
$filename = time().'-'.$uploaded_file->getClientOriginalName();
Storage::disk('local')->putFileAs(
'student_document_files/'. \Auth::user()->userable_id .'/'. $student_id .'/',
$uploaded_file,
$filename
);
$student_document = new StudentDocument;
$student_document->filename = $filename;
$student_document->student_document_type_id = StudentDocumentType::where('student_document_type','Passport')->first()->id;
$student_document->original_filename = $uploaded_file->getClientOriginalName();
$student_document->mime = $uploaded_file->getMimeType();
$student_document->student_id=$student_id;
$passport->student_documents()->save($student_document);
}
return redirect('/baadmin/students/'. $student_id .'#kt_tabs-passports')->with('flash_message', ['success','Created Successfully','Student Passport "'. $request->input('passport_number') .'" created successfully!']);
}
Error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'student_documentables' doesn't exist
INSERT INTO `student_documentables` (
`student_document_id`,
`student_documentable_id`,
`student_documentable_type`
)
VALUES
(5, 503, App \ StudentPassport)
I took the example as found in the Laravel Documentation here and just renamed 'tag' to student_documents' essentially. The student_documentable table doesnt exist of course, as it should be plugging it into the student_documents table.
Schema:
CREATE TABLE `student_documents` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`student_id` bigint(20) unsigned NOT NULL,
`student_document_type_id` bigint(20) unsigned NOT NULL,
`filename` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`mime` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`original_filename` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`primary_date` datetime NOT NULL DEFAULT current_timestamp(),
`secondary_date` datetime DEFAULT NULL,
`student_documentable_id` bigint(20) DEFAULT NULL,
`student_documentable_type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `student_id_index` (`student_id`),
KEY `student_document_type_id_index` (`student_document_type_id`),
KEY `student_documentable_id_index` (`student_documentable_id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
What am I doing wrong or is Laravel just not understanding 'student_documents'?
I can of course change the Student Passport Class to not plug into 'student_documentable' and use 'student_document' then it would try put in the data to the correct table, but I dont know if this is right since all documentation refers to adding an 'able' at the end...
You should first create a pivot table for that MorphMany relation,
Schema::create('student_documentables', function (Blueprint $table)
{
// optional depends if you want an id or not
$table->id();
// here singular is used, to generate student_documentable_type and student_documentable_id fields
$table->morphs('student_documentable');
// the foreign key to student_document
$table->unsignedInteger('student_document_id');
$table->foreign('student_document_id')->on('student_documents')->references('id')->onUpdate('cascade')->onDelete('cascade');
});
In your StudentDocument :
// we define a relation to retrieve all documentables like passport that are linked to that document
public function student_documentables()
{
return $this->morphTo('student_documentables');
}
In your StudentPassport :
// we define a relation to retrieve all documents linked to that passport
public function student_documents()
{
return $this->morphMany(StudentDocument::class, 'student_documentables');
}

Cakephp 3.9 Multiple Associations with the Same Table

I am working on Cakephp 3.9 and I have 3 tables in my application.
CREATE TABLE `leads` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`first_name` varchar(100) NOT NULL,
`last_name` varchar(100) NOT NULL,
`created_dt` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
CREATE TABLE `clients` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`lead_id` int(10) NOT NULL,
`first_name` varchar(100) NOT NULL,
`last_name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
CREATE TABLE `contacts` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`lead_id` int(10) NOT NULL,
`first_name` varchar(100) NOT NULL,
`last_name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
There is a one-to-one relation between the Leads, Clients, and Contacts tables.
I also have a Users table:
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
... and a leads_users table
CREATE TABLE `leads_users` (
`lead_id` int(10) NOT NULL,
`user_id` int(10) NOT NULL,
`user_type` enum('Opener','Closer') NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin
In my application, the users can be associated with the leads as Openers and Closers. There can be multiple openers and multiple closers for a lead. I have defined my models as below:
class LeadsTable extends Table
{
public function initialize(array $config)
{
$this->hasOne('Contacts');
$this->hasOne('Clients');
$this->belongsToMany('Users', [
'className' => 'Users'
])
->setConditions(['LeadsUsers.user_type' => 'Opener'])
->setProperty('openers');
$this->belongsToMany('Users', [
'className' => 'Users'
])
->setConditions(['LeadsUsers.user_type' => 'Closer'])
->setProperty('closers');
}
}
class ClientsTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('Leads');
}
}
class ContactsTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('Leads');
}
}
class UsersTable extends Table
{
public function initialize(array $config)
{
$this->belongsToMany('Leads');
}
}
Now in the Leads controller, I am trying to find all the leads, their contacts, clients, openers, and closers by using the below line of code.
$result = $this->Leads->find('all')->contain(['Contacts', 'Clients', 'Users']);
With this, I am getting the closers but not the openers. In the LeadsTable, if I comment out the closers association then I am getting the openers. So looks like the second association is overwriting the first one.
If I change the LeadsTable association to below:
class LeadsTable extends Table
{
public function initialize(array $config)
{
$this->hasOne('Contacts');
$this->hasOne('Clients');
$this->belongsToMany('Openers', [
'className' => 'Users'
])
->setConditions(['LeadsUsers.user_type' => 'Opener'])
->setProperty('openers');
$this->belongsToMany('Closers', [
'className' => 'Users'
])
->setConditions(['LeadsUsers.user_type' => 'Closer'])
->setProperty('closers');
}
}
I am getting the below error:
The Users association is not defined on Leads.
Please let me know what am I doing wrong.
Thank you in advance.
You're getting this error because you have to change your controller query as well.
$result = $this->Leads->find('all')->contain(['Contacts', 'Clients', 'Closers','Openers']);
So I ended up changing my database a bit and that fixed it.
CREATE TABLE `leads_openers` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`lead_id` int(10) NOT NULL,
`opener_id` int(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
CREATE TABLE `leads_closers` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`lead_id` int(10) NOT NULL,
`closer_id` int(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
and then I changed my models like below:
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class OpenersTable extends Table
{
public function initialize(array $config)
{
$this->setTable('users');
$this->setPrimaryKey('id');
$this->belongsToMany('Leads', [
'joinTable' => 'leads_openers',
]);
}
}
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class ClosersTable extends Table
{
public function initialize(array $config)
{
$this->setTable('users');
$this->setPrimaryKey('id');
$this->belongsToMany('Leads', [
'joinTable' => 'leads_closers',
]);
}
}
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class LeadsTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
$this->belongsToMany('Openers', [
'joinTable' => 'leads_openers',
])
->setProperty('openers');
$this->belongsToMany('Closers', [
'joinTable' => 'leads_closers',
])
->setProperty('closers');
}
}
and then in the controller, I am doing this:
$leads = $this->Leads->find('all')
->where(['Leads.status IN' => $search_condition_lead])
->contain(['Contacts', 'Openers', 'Closers']);

PHP function stripos() expects parameter 1 to be string, object given

I'm running through Laravel 6.0's addSelect() method as the official documentation described. However, I'm getting an error as my title suggested.
Two corresponding tables are:
CREATE TABLE `destinations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(90) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1
CREATE TABLE `flights` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(90) NOT NULL,
`destination_id` int(11) NOT NULL,
`arrived_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
The corresponding controller:
use App\Flight;
use App\Destination;
class TestController extends Controller
{
public function Index()
{
return Destination::addSelect(['last_flight' =>
Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
])->get();
}
}
What is the object provided as a parameter, which is responsible for the error?

Why Laravel does not insert row?

Inserting operation is:
$r = [
'idVisitor' => 4,
'lastname' => 'fg',
'firstname' => '11',
'document_number' => '444',
'idEvent' => 2
];
$g = Visitor::insert($r);
Model is:
class Visitor extends Model
{
public $timestamps = false;
protected $table = 'visitors';
protected $fillable = ['firstname', 'lastname', 'middlename', 'document_number', 'pincode', 'ckecked', 'date_cheked', 'user_cheked', 'company', 'code', 'status', 'idEvent', 'date', 'birthday'];
protected $primaryKey = 'idVisitor';
}
if to do this:
$g = Visitor::insert($r);
var_dump($g);
It gives me true, so Laravel considers that it was committed.
Dump SQL is:
CREATE TABLE `visitors` (
`idVisitor` int(11) NOT NULL,
`firstname` varchar(45) NOT NULL,
`lastname` varchar(45) NOT NULL,
`middlename` varchar(45) DEFAULT NULL,
`document_number` varchar(45) NOT NULL,
`pincode` varchar(10) DEFAULT NULL,
`ckecked` int(11) DEFAULT '0',
`date_cheked` date DEFAULT NULL,
`user_cheked` int(11) DEFAULT NULL,
`company` varchar(100) DEFAULT NULL,
`code` varchar(100) DEFAULT NULL,
`idEvent` int(11) NOT NULL,
`status` int(11) NOT NULL DEFAULT '1',
`date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`birthday` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `visitors`
--
ALTER TABLE `visitors`
ADD PRIMARY KEY (`idVisitor`,`idEvent`),
ADD UNIQUE KEY `document_number_UNIQUE` (`document_number`),
ADD UNIQUE KEY `pincode_UNIQUE` (`pincode`),
ADD UNIQUE KEY `code_UNIQUE` (`code`),
ADD KEY `fk_visitors_users1_idx` (`user_cheked`),
ADD KEY `fk_visitors_events1_idx` (`idEvent`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `visitors`
--
ALTER TABLE `visitors`
MODIFY `idVisitor` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=18;
--
-- Constraints for dumped tables
--
--
-- Constraints for table `visitors`
--
ALTER TABLE `visitors`
ADD CONSTRAINT `fk_visitors_events1` FOREIGN KEY (`idEvent`) REFERENCES `events` (`idEvent`) ON DELETE NO ACTION ON UPDATE NO ACTION,
ADD CONSTRAINT `fk_visitors_users1` FOREIGN KEY (`user_cheked`) REFERENCES `users` (`idUser`) ON DELETE NO ACTION ON UPDATE NO ACTION;
COMMIT;
I use MySQL 5.0.12
Also I tried:
$g = new Visitor();
$g->lastname = 4;
$g->middlename = 4;
$g->document_number = 444;
$g->firstname = 4;
$g->idEvent = 2;
$g->save();
please use
$g = new Visitor();
$g->idVisitor = 4;
$g->firstname= 4;
$g->idVisitor = 4
$g->save();
this is another way
$g = new Visitor(); call class without '()', so $g = new Visitor;

Laravel 5.4 Model Relationship

I have created three tables users, courses and user_courses as shown below
CREATE TABLE `users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`status` enum('0','1') COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
CREATE TABLE `courses` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`title` text,
`description` text,
`price` varchar(255) DEFAULT NULL,
`schedule` varchar(255) DEFAULT NULL,
`duration` varchar(255) DEFAULT NULL,
`summary` text,
`skills` text,
`mode` enum('0','1') DEFAULT NULL COMMENT '0-Online 1 -Instructor',
`random_token` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
CREATE TABLE `user_courses` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL,
`course_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
Now with these tables, I want to bind relationship such as when I fetch Users I'm able to get courses for a user and when I get to courses I want users associated with the course.
Please help how I can achieve it.
I have got my answer so posting it here if it can help anyone.
The main thing here is to assign a many-to-many relationship. In my user model I have defined
public function courses()
{
return $this->belongsToMany('App\Course');
}
In course model
public function users()
{
return $this->belongsToMany('App\User');
}
Actually, it depends on how you want to use the relationship.In some parts of the code you will need $user->courses or more likely to query $course->users or both.
Now here user_course table will be assumed as a pivot table. So in model, you can write it as
public function courses()
{
return $this->belongsToMany('App\Course', 'user_courses');
}
Here you can also specify the actual names of the fields of that particular pivot table i.e user_courses table.Then, what all we have to do is just add two more parameters first is the current model field and then add the field of the model being joined like
public function courses()
{
return $this->belongsToMany('App\Course', 'user_courses','user_id', 'course_id');
}
So using the above model relationship you can easily fetch users with all the respective courses by
User::->with('courses')->get();
First Fix
First, fix the user_course table structure the users table has id as integer while it's being referenced in user_course in user_id as bigint.
Solution
The first thing to do is to create models in the application. Then create relationships within models and finally use those relationships.
Create models
By using command line
php artisan make:model Course
php artisan make:model UserCourse
You can create them manually if you like. By default, they will be created in app folder with namespace App. For example, the user model will be App/User and so on.
The user model will already exists as its shipped with laravel default installation.
Create relationships
In user model add the following function
public function courses()
{
$this->belongsToMany(Course::class, 'user_course');
}
You can leave the Course Model empty if you are not planning to make a reverese relation from course to user. The one above defines relation from user to courses
Usage
Say in a controller you can use this as
public function someFunctionInController()
{
$usersWithCourses = \App\User::with('courses')->get()->toArray();
//result of a single user record will look something like this
/**
[
'id' => 1,
'name' => 'some name'
... //other users table columns
//the courses will be an array of array content and will be automatically injected
'courses' => [[
'id' => 1 //the course id
... //course table columns,
'pivot' => [
'user_id' => 1,
'course_id' => 1
]
],[
'id' => 3 //the course id
... //course table columns,
'pivot' => [
'user_id' => 1,
'course_id' => 3
]
]]
]
**/
}

Resources