Yii Model with composite primary key - php

My MySQL table's primary is a composite of 2 columns: space_id (INTEGER) and day (DATE).
CREATE TABLE `ck_space_calendar_cache` (
`space_id` int(11) NOT NULL,
`day` date NOT NULL,
`available` tinyint(1) unsigned NOT NULL DEFAULT '0',
`price` decimal(12,2) DEFAULT NULL,
`offer` varchar(45) DEFAULT NULL,
`presale_date` date DEFAULT NULL,
`presale_price` decimal(12,2) DEFAULT NULL,
`value_x` int(11) DEFAULT NULL,
`value_y` int(11) DEFAULT NULL,
PRIMARY KEY (`space_id`,`day`),
KEY `space` (`space_id`),
CONSTRAINT `space` FOREIGN KEY (`space_id`) REFERENCES `ck_space` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
It works fine in raw SQL, it complains if I try to create a duplicate, but lets me create rows the the same day or the same space_id.
However, in Yii when using new Object() and save(), it complains as if "space_id" has to be unique.
I used "Giix" to generate the model if it matters.
I tried to add this code to the model, but it didn't help:
public function primaryKey(){
return array('space_id', 'day');
}

Adding this code to your ActiveRecord class is okay, but should not be necessary because Yii already has that information from your MySQL table declaration.
public function primaryKey(){
return array('space_id', 'day');
}
When Yii complains about "space_id" to be unique, giix might have added a validation rule to rules() in your ActiveRecord class. These rules are checked before an ActiveRecord is saved and it will only save if all rules are okay. Read the Data Validation section of Definitive Guide for more information.

From what I understand since Yii 1.1 composite primary keys are not longer supported with Gii, which is frustrating many developers. There are other poorly documented alterations needed in your code aside from the returning an array as a primary key.
The best explanation I found was in this discussion in the Yii forum.

Related

How do I avoid ambiguous naming between my PHP-code and an SQL junction table?

Given the following database schema (MySQL), I'm worried about possibly introducing ambiguous naming in its PHP 5.5 model class given below (abstracted for simplicity).
CREATE TABLE `catalog_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`catalog_id` int(11) NOT NULL DEFAULT '0'
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `catalog_item_property` (
`catalog_item_id` int(11) NOT NULL DEFAULT '0',
`catalog_item_properties_id` int(11) NOT NULL DEFAULT '0'
PRIMARY KEY (`catalog_item_id`,`catalog_item_properties_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `catalog_item_properties` (
`id` int(11) NOT NULL AUTO_INCREMENT
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `catalog_item_properties_description` (
`catalog_item_properties_id` int(11) NOT NULL DEFAULT '0',
`language_id` int(11) NOT NULL DEFAULT '0'
PRIMARY KEY (`catalog_item_properties_id`,`language_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Some background on the schema:
catalog_item holds items from a catalog.
catalog_item_property holds all properties belonging to a catalog_item.
catalog_item_properties holds a list of all possible item properties.
catalog_item_properties_description holds a multi-language description for all catalog_item_properties.
I'm following a singular naming convention for all my tables.
The interface function names that I am unsure of:
public function getItemProperty($id)
{
// Get all catalog item properties that belong to this catalog item?
}
public function getItemProperties()
{
// Get list of all possible catalog item properties?
}
As you can see, on my current iteration, getItemProperty may return more than one property. But so does getItemProperties. Ideally, I would like the function naming to speak for themselves, and for there to be no ambiguity at all. I would also like there to be a strong relation between the function names and the schema table names, for the sake of consistency.
Some questions I would love to hear some informed opinions on:
Do I change either the table names, or the function names (or both?) to resolve this potential ambiguity? Or, might there be no ambiguity at all?
Is it ok to mix the singular and plural form on table names in this scenario?
I'm pretty new to database design. If I completely missed the best practice approach here, please do let me know. Thank you in advance for sharing your thoughts!

MYSQL table and column naming convention

I am curious to know what is best naming convention in terms of performance for mysql table names and column names. I am designing a new database for my project.
What I have used so far is use descriptive table/column names which sometimes seems long but I think it helps in easily understanding the use/function of a table.
For example see below DDL:
CREATE TABLE `product_configuration` (
`product_configuration_id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(20) NOT NULL,
`product_size_id` int(20) NOT NULL,
`product_color_id` int(20) NOT NULL,
`price` float NOT NULL,
`image` varchar(255) DEFAULT NULL,
`locked` tinyint(1) DEFAULT '0' COMMENT '1=locked, 0 =unlocked. if locked then this row can''t be deleted/updated',
`active` tinyint(1) DEFAULT '1' COMMENT '1=active, 0=inactive and wont display on frontend',
PRIMARY KEY (`product_configuration_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2342 DEFAULT CHARSET=latin1
And another DDL in which I use the primary key from above DDL as foreign key :
CREATE TABLE `product` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`product_name` varchar(255) NOT NULL,
`product_description` varchar(255) NOT NULL,
`product_image` varchar(255) NOT NULL,
`price` float NOT NULL,
`active` tinyint(1) NOT NULL COMMENT '1=active, 0=inactive',
`date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`product_type_id` int(11) DEFAULT NULL,
`date_modified` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=latin1
Basically I use singular table names with table name as prefix in most of the column names inside that table and I keep the same name and datatype for primary and foreign keys so that I can easily know which foreign key relates to which primary key/tables.
But I wonder, do using long table/column names have performance impact when database size grows. Like instead of just using "id" as primary key I am using long "product_configuration_id".
Also if I name tables/columns in uppercase and lowercase mixed like
"ProductConfiguration"
for table name and
"ProductConfigurationId"
for column name will that have any performance impact or linux/windows environment compatibility issue.
Long table and column names do not have (any significant) performance impact. All tables and column references are turned into internal locators during the compilation phase of the query. So pretty much the only impact is having to query a longer query string. The parsing part of query compilation is usually ignored from a performance perspective.
The following is opinion-based. As a general rule, I follow these conventions for naming:
Table names are in the plural, because they contain multiple entities.
Each table (almost always) has an auto-incremented numeric primary key, which is the singular form of the table followed by Id.
This column is the first column defined, so I can use order by 1 desc to get the most recent rows added to the table.
The table name is not (generally) part of the column name. I always (try to) use table aliases, so including the table name would be redundant.
Foreign key references use the same column name as the primary key they are referring to, when possible, so I can use using for joins.
I admit that these are "opinion-based", so the real answer to your question is in the first paragraph.

Using AJAX to create a like and delike function with php and mysql

First of all, i have searched, and I have asked this question once before:
Creating a Like function php mysql ajax
The DB problem in that thread was solved. And i followed the instructions to create a second thred, for the AJAX problem.
I found this jQuery : Changing class of button with AJAX call, and it looks simpel and very nice. And i have also followed this http://pluscss.com/tutorials/ajax-like-script-using-php-mysql-jquery tutorial. But I'm having problems combine them.
This is what I'm trying to do:
count the current amount of likes
check if the user have liked it before
give the user the option to like (or delike if previously liked)
with ajax and like.php as a respons page (or whatever)
Im trying to create a function that will return the current amount of likes (I'm no good at creating functions, sorry bout that)
function like($post_id,$comment_id)
{
$count = $mysqli->query("SELECT COUNT(*) as TOTAL_COMMENT_LIKES FROM `comments_likes`
WHERE comment_id_fk='".$comment_id."'");
if $row_array=$count->fetch_array(MYSQLI_ASSOC); < 0
then echo "be the first to like"
else
check if user already have liked it
give option to delike
if not have liked
then give option to like
return $TOTAL_COMMENT_LIKES or $TOTAL_POST_LIKE
}
I have two tables, one for the liked posts and one for liked comments
CREATE TABLE IF NOT EXISTS `post_likes` (
`like_id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`post_id_fk` INT(11),
`uid_fk` int(11) NOT NULL,
`date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`ip` varchar(39) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0.0.0.0',
FOREIGN KEY (post_id_fk) REFERENCES posts(post_id),
FOREIGN KEY (uid_fk) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
CREATE TABLE IF NOT EXISTS `comment_likes` (
`like_id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`comment_id_fk` INT(11),
`uid_fk` int(11) NOT NULL,
`date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`ip` varchar(39) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0.0.0.0',
FOREIGN KEY (comment_id_fk) REFERENCES comments(comment_id),
FOREIGN KEY (uid_fk) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
And to top it of with AJAX:
function like(_element){
if($(_element).hasClass('unlike')){
$.ajax(); //do unlike
$(_element).removeClass('unlike'); // this goes inside the success:function(){} of the ajax call
}else{
$.ajax(); //do like
$(_element).addClass('unlike'); // this goes inside the success:function(){} of the ajax call
}
}
All help is very much appreciated!

Zend Framework 2 with zfc-rbac database population

There are numerous 'getting started' tutorials out there on how to implement zfc-user and zfc-rbac into Zend Framework 2. The github pages for zfc-user and zfc-rbac (https://github.com/ZF-Commons) are clear and the implementation is indeed pretty easy (as stated on many of the tutorials). I also found the SQL schemes which are needed for both zfc-user and zfc-rbac (/vendor/zf-commons/zfc-[user/rbac]/data/).
The creation of a user into the database is easy, since zfc-user already sets this up for you (http://example.com/user). Everything fine so far. Now I want to populate the roles, but it's not clear to me on how to populate the rbac tables correctly. The lack on information about this surprises me, since the zfc-rbac component is a popular module for the Zend Framework.
I understand the principal of Role Based Access Control and the population of the tables for the permissions and the table linking the permissions and roles together are clear, it's the role table that's not clear to me. I understand that you can have a role which has a parent role, but it's not clear how to populate the table with a parent role since there is a foreign key constraint which states the 'parent_role_id' has to be a 'role_id'.
Below is the SQL for the role table (this is the SQL provided by zfc-rbac):
CREATE TABLE `rbac_role` (
`role_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`parent_role_id` int(11) unsigned NOT NULL,
`role_name` varchar(32) NULL,
PRIMARY KEY (`role_id`),
KEY `parent_role_id` (`parent_role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
ALTER TABLE `rbac_role`
ADD CONSTRAINT `rbac_role_ibfk_1` FOREIGN KEY (`parent_role_id`) REFERENCES `rbac_role` (`role_id`);
With the foreign key in place adding a parent role seems impossible?
INSERT INTO `rbac_role` (parent_role_id, role_name) VALUES (NULL, 'admin');
Basically my question is (and I feel very stupid for asking this) but how does an insert for a parent role look like? And if the insert statement I presented is in fact correct, do I always need to remove the foreign key before inserting a parent role?
Change your create table to the following:
CREATE TABLE `rbac_role` (
`role_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`parent_role_id` int(11) unsigned NULL,
`role_name` varchar(32) NULL,
PRIMARY KEY (`role_id`),
KEY `parent_role_id` (`parent_role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
Notice that parent_role_id is NULL instead of NOT NULL. If parent_role_id is NOT NULL then that means that it has to have a parent but since the foreign key reference is to the same table there is no way to insert a parent row!
fyi: This issue has been fixed. Version 0.2.0 of zfc-rbac will allow NULL value as parent_role_id

Save associated data into database with cakephp and HABTM

I have 2 tables: release_servers and release_components
I have a link table release_server_to_components
I right now have it so that each server can have multiple components and that each component can be on multiple servers.
The following are the create statements:
CREATE TABLE `release_components` (
`id` int(11) NOT NULL auto_increment,
`buildID` varchar(45) default NULL,
`svnNumber` varchar(45) NOT NULL,
`componentType` varchar(45) NOT NULL,
`release_id` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM
CREATE TABLE `release_servers` (
`id` int(11) NOT NULL auto_increment,
`server_name` varchar(45) NOT NULL,
`server_environment` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM
Link table:
CREATE TABLE `release_server_to_components` (
`id` int(11) NOT NULL auto_increment,
`release_component_id` int(11) NOT NULL,
`release_server_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM
What I want to add is the ability to have a system ID -- this system ID would be per component on a server (not per server and not per component, but per the component on each server). I want to be able to easily add the system ID per component and insert it into the database.
I can provide code for models and controllers if needed.
You want to "fake" Ruby's through association with CakePHP.
Why do this over HABTM?
Because you want to save data about the association. With Cake's HABTM saving data about the association is difficult, because the only thing you really have is a JOIN table. You need something more powerful than this.
First, get rid of the $hasAndBelongsToMany property in your model. Next we'll be refitting your release_server_to_components table as your "through" table.
So, in ReleaseComponent and ReleaseServer model you would have an association like this:
$hasMany = array('ReleaseServerToComponent');
Now, in your new ReleaseServerToComponent model you would have an association like this:
$belongsTo = array('ReleaseComponent', 'ReleaseServer');
Now, you can access this table just like a normal Cake model, ie $this->ReleaseServer->ReleaseServerToComponent->find(). You can add additional fields to the through table, like server_component_name. You already have a unique identifier for specific, server components with the primary key of the release_server_to_components table.
You could save this data using Cake's saveAll() method. Alternatively you could generate your own data to save, simply plugging in the server ID and component ID from form fields. At the top of that link is the format saved data should be in when you pass it to the model's save method.

Categories