How can I model an is-a relationship with DBIx::Class? - database

With the following (simplified) MySQL table definitions:
create table items (
item_id int unsigned auto_increment primary key,
purchase_date date
) engine = innodb;
create table computers (
item_id int unsigned primary key,
processor_type varchar(50),
foreign key item_fk (item_id) references items (item_id)
on update restrict on delete cascade
) engine = innodb;
create table printers (
item_id int unsigned primary key,
is_duplex boolean,
foreign key item_fk (item_id) references items (item_id)
on update restrict on delete cascade
) engine = innodb;
Being new to DBIx::Class, I would like to model an inheritance relationship between database entities (computers and printers both being items), but with the provided belongs_to relationship type, this seems to be awkward, because the association with the base class is not hidden, so one must still manually create entities for both classes, and access to base class attributes in the derived classes is different from accessing their own attributes.
Is there an elegant solution which would allow me to say:
$printer = $printer_rs->create({purchase_date => $date, is_duplex => 0});
or (on a fetched printer row):
$date = $printer->purchase_date;
$duplex = $printer->is_duplex;
?

You can use the proxy attribute on a relationship to enable the accessors -- it's documented in add_relationship of DBIx::Class::Relationship::Base and you can use it with belongs_to like:
__PACKAGE__->belongs_to(
'item' => 'MyApp::Schema::Item',
'item_id',
{ proxy => [ qw/purchase_date/ ] }
);
which will make all of your Printer objects have purchase_date accessors that refer to the associated Item object.
As for create, you can't do it without overriding new_result, which is actually pretty easy. You just have to take advantage of the create-from-related behavior to turn
->create({
is_duplex => 1,
purchase_date => $dt,
})
into
->create({
is_duplex => 1,
item => {
purchase_date => $dt,
},
})
Or you could just foist the knowledge of what columns are in item off on your users and have them provide that hashref directly ;)

Related

Update rows if their keys are in a set

I have a table in SQL Server with a composite primary key and a set of keys of rows that need to be updated. Is it possible to update these rows (e.g. do thing like this) using Entity Framework Plus or anything else?
I want something like
ctx.Users.Where(x => MySet.Contains(new { x.Col1, x.Col2 }))
.Update(x => new User() { IsSoftDeleted = 1 });

How to create more than one reference to a table in cakephp?

Let's say I have two tables:
CREATE TABLE drinks (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
morning_drink_id INT,
evening_drink_id INT
);
How can I make those drink_id references valid?
I’ve tried:
adding a view
CREATE VIEW morning_drinks AS SELECT * FROM drinks;
CREATE VIEW evening_drinks AS SELECT * FROM drinks;
adding foreign keys
FOREIGN KEY morning_drink_key (morning_drink_id) REFERENCES drinks(id),
FOREIGN KEY evening_drink_key (evening_drink_id) REFERENCES drinks(id)
bake craches in both cases... Is there a proper way?
public $belongsTo = array(
'Drink' => array(
'className' => 'Drink',
'foreignKey' => 'morning_drink_id'
)
);
this you can add to Users model. And this is how to do it.
edit:
Bake is expecting tables for your two foreign keys, and it’s complaining about them. I got the same error as you when I deleted the ‘morning_drinks’ view, else it created all. Add those two views and try again.

CakePHP hasAndBelongsToMany vs hasMany through

This is only my 3rd CakePHP app, and the first to require a HABTM type association. I'm working with Cake 2.1, in which there was a change to HABTM parameters allowing for extra information to be saved by setting the 'unique' key to 'keepExisting'. In a normal HABTM association you have a table with 2 fields, each foreign keys. I think I need one with 3 foreign keys. Is that possible by using the 'unique' key, or should I use a hasMany through association?
Here's my schema:
/* repair_orders */
CREATE TABLE repair_orders (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
number VARCHAR(16), //Auto-generated repair order number
created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL
);
/* Employees */
CREATE TABLE employees (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(16),
created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL
);
/* op_codes */
CREATE TABLE op_codes (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(16),
sale_material DECIMAL(10,2),
cost_material DECIMAL(10,2),
created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL
);
The op codes are a essentially a list of items and their cost.
A repair order can have many op codes. I also need to keep track of which employee is assigned to the specific op codes on each repair order. So I think that requires another table, something like:
/* repair_order_assignments */
CREATE TABLE repair_order_assignments (
repair_order_id INT(16),
op_code_id INT(16),
employee_id INT(16)
);
So my associations would be:
Repair_Order_Assignment hasMany Employee, Op_Code
Employee belongsTo Repair_Order_Assignment
Op_Code belongsTo Repair_Order_Assignment
Repair_Order_Assignment hasAndBelongsToMany Repair_Order
Repair_Order hasAndBelongsToMany Repair_Order_Assignment
According to the manual, when hasAndBelongsToMany associations are saved, the association is deleted first. You lose the extra data in the columns as it is not replaced in the new insert. However, it goes on to note that in 2.1 there is a unique variable that can be set to save the extra data. Will this setup work with the unique key or should I use a hasMany through approach?
I've found the only thing that a HABTM relationship for is tags and multiple categories.
Any sort of membership, friendship or following relationship I use has many through because sometimes it's nice to attach extra data to a relationship, even if it's just created/modified. Another thing that puts me off HABTM is the replace-everything-upon-update logic. Causes a lot of bother saving different parts of a model.

How to Model Ternary Relationship in CakePhp?

Page table
(1)
|
|
(*)
User_Moderate_Page (*)----------- (1)Access_level table
(*)
|
|
(1)
User table
How do i Model such a ternary relationship in CakePhp?
User to Page can be modelled using the hasBelongtoMany Relationship. But User_Moderate_page is just an association table, should I even write a Model for User_Moderate_Page in Cake?
I'm not sure CakePHP accepts this, but what you should do is create the table with a primary key and the 3 foreign keys. Somewhat like this:
CREATE TABLE IF NOT EXISTS `mydb`.`access_levels_pages_users` (
`id` INT NOT NULL AUTO_INCREMENT ,
`page_id` INT NOT NULL ,
`access_level_id` INT NOT NULL ,
`user_id` INT NOT NULL ,
PRIMARY KEY (`id`) ,
INDEX `fk_access_levels_pages_users_pages` (`page_id` ASC) ,
INDEX `fk_access_levels_pages_users_access_levels1` (`access_level_id` ASC) ,
INDEX `fk_access_levels_pages_users_users1` (`user_id` ASC) ,
CONSTRAINT `fk_access_levels_pages_users_pages`
FOREIGN KEY (`page_id` )
REFERENCES `mydb`.`pages` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_access_levels_pages_users_access_levels1`
FOREIGN KEY (`access_level_id` )
REFERENCES `mydb`.`access_levels` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_access_levels_pages_users_users1`
FOREIGN KEY (`user_id` )
REFERENCES `mydb`.`users` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
Now, I'm not sure Cake will bake this table, so you might have to try and make the models by hand, this table would have belongsTo the 3 other tables. The other 3 tables non association tables will have to have a HMABTM relationship with the other 2, as in users HMABTM access_levels and pages and so on.
Again, not sure if it will work. I would suggest maybe trying to see if you can model it in a different manner.
Let me generalize it by describing it with generic tables:
Lets say you have 3 tables - firsts, seconds, thirds - each with primary keys 'id'.
let your join table be called 'firsts_seconds_thirds' with foreign keys to each main table:
first_id
second_id
third_id
<additional fields of this association table>
usually we define HABTM relationships between tables, in this case we need to create a cake model for the join table - lets call it FirstsSecondsThird (By cakePhp's naming conventions)
The relationship between models you need to define is:
First hasMany FirstsSecondsThird
Second hasMany FirstsSecondsThird
Third hasMany FirstsSecondsThird
FirstsSecondsThird belongsTo First,Second,Third
The need for this is explained here - Associations: Linking Models Together
Code for the same:
class First extends AppModel {
public $hasMany = array(
'FirstsSecondsThird' => array(
'className' => 'FirstsSecondsThird',
'foreignKey' => 'first_id'
)
);
}
//Same for classes 'Second' and 'Third'
class FirstsSecondsThird extends AppModel {
public $belongsTo = array(
'First' => array(
'className' => 'First',
'foreignKey' => 'first_id'
),
'Second' => array(
'className' => 'Second',
'foreignKey' => 'second_id'
),
'Third' => array(
'className' => 'Third',
'foreignKey' => 'third_id'
)
);
}
The models are perfectly setup, but now inserting/updating/deleting from the main/join table should be done correctly or they are of no use.
Model::saveMany() and Model::saveAssociated(), using the option 'deep' etc. need to be used. Read about it here. You also need to think about ON DELETE RESTRICT/CASCADE for these join tables and model them appropriately.
Look up ACL. It is complicated, but it is what you need (especially if you need the flexibility in setting up who can moderate what page)

is it safe to refactor my django models?

My model is similar to this. Is this ok or should I make the common base class abstract? What are the differcenes between this or makeing it abstract and not having an extra table? It seems odd that there is only one primary key now that I have factored stuff out.
class Input(models.Model):
details = models.CharField(max_length=1000)
user = models.ForeignKey(User)
pub_date = models.DateTimeField('date published')
rating = models.IntegerField()
def __unicode__(self):
return self.details
class Case(Input):
title = models.CharField(max_length=200)
views = models.IntegerField()
class Argument(Input):
case = models.ForeignKey(Case)
side = models.BooleanField()
is this ok to factor stuff out intpu Input? I noticed Cases and Arguments share a primary Key.
like this:
CREATE TABLE "cases_input" (
"id" integer NOT NULL PRIMARY KEY,
"details" varchar(1000) NOT NULL,
"user_id" integer NOT NULL REFERENCES "auth_user" ("id"),
"pub_date" datetime NOT NULL,
"rating" integer NOT NULL
)
;
CREATE TABLE "cases_case" (
"input_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "cases_input" ("id"),
"title" varchar(200) NOT NULL,
"views" integer NOT NULL
)
;
CREATE TABLE "cases_argument" (
"input_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "cases_input" ("id"),
"case_id" integer NOT NULL REFERENCES "cases_case" ("input_ptr_id"),
"side" bool NOT NULL
)
From: django web site
Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class. This model will then not be used to create any database table. Instead, when it is used as a base class for other models, its fields will be added to those of the child class.

Resources