I dont suppose anyone has a copy of datamapper 1.3.3 hanging around?!
I need to run this on a PHP4 server and it appears that the autoload call used in later versions if PHP5 only!
Mike
This is an archived forum and the content is probably no longer relevant, but is provided here for posterity.
The active forums are here.
September 05, 2008 12:32pm
Subscribe [115]#691 / Apr 15, 2009 3:35pm
I dont suppose anyone has a copy of datamapper 1.3.3 hanging around?!
I need to run this on a PHP4 server and it appears that the autoload call used in later versions if PHP5 only!
Mike
#692 / Apr 15, 2009 4:39pm
I am having a problem trying to handle an Duplicatation error, based on the Table Schema below:
CREATE TABLE IF NOT EXISTS `seasons` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`zone_id` mediumint(9) NOT NULL,
`seq` smallint(5) unsigned NOT NULL,
`gametitle_id` mediumint(9) NOT NULL,
`seamode_id` tinyint(4) NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `zoneseq` (`zone_id`,`seq`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT AUTO_INCREMENT=4 ;
--
-- Dumping data for table `seasons`
--
INSERT INTO `seasons` (`id`, `zone_id`, `seq`, `gametitle_id`, `seamode_id`, `created`, `updated`) VALUES
(1, 1, 1, 1, 0, '2009-04-15 19:15:10', '2009-04-15 19:15:10'),
(2, 2, 1, 1, 0, '2009-04-15 19:16:09', '2009-04-15 19:16:09');The duplication keys around:
UNIQUE KEY `zoneseq` (`zone_id`,`seq`)
db_debug has been set to FALSE
and I’m using the following method in a model (Season) to attempt custom error handling.
class Season extends Datamapper {
var $table = 'seasons';
var $has_many = array('commissioner', 'registration', 'conference');
var $has_one = array('zone');
function Season()
{
parent::Datamapper();
}
function update($data=NULL)
{
$ds = new Season();
$ds = $this->get_by_id($data['id']);
if ( $ds->exists() )
{
$zone_id = $ds->zone_id;
$seq = $ds->seq;
$dup_zone_id = ($zone_id == (integer) $data['zone_id']);
$dup_seq = ($seq == (integer) $data['seq']);
if ( $dup_zone_id == $dup_seq )
return array('success' => FALSE, 'message' => "Record already exist for Zone ID: $zone_id and Season #: $seq");
$s = $this->get_by_id($data['id']);
$s->zone_id = $data['zone_id'];
$s->seq = $data['seq'];
$s->gametitle_id = $data['gametitle_id'];
if ( $s->save() )
return array('success' => TRUE, 'message' => "Record successfully updated.");
else
return array('success' => FALSE, 'message' => "Unable to update record at this time.");
}
/* else
{
return $this->create($data);
} */
}
}
Is there a better method of handling duplication on indexes, and reporting this back to the user?
The array that is returned is processed by a controller which pushes it into the appropriate view.
#693 / Apr 15, 2009 4:45pm
DM has a built-in unique rule. You should use that, which will check for uniqueness on a column before attempting to save. It is explained in the docs.
[FYI: If you are using DMZ, then you can’t have NOT NULL for “<model>_id” columns, because the model needs to be saved before the relationship. Also, instead of hand-setting the <model>_ids, why don’t you use the normal relationship saving methods?]
#694 / Apr 15, 2009 4:55pm
DM has a built-in unique rule. You should use that, which will check for uniqueness on a column before attempting to save. It is explained in the docs.
Well, I am not looking for a unique column individually, but unique combination of the two columns.
In other words, you cannot have a duplicate season with the same zone and seq value. But I will re-check the docs if this is the case.
[FYI: If you are using DMZ, then you can’t have NOT NULL for “<model>_id” columns, because the model needs to be saved before the relationship. Also, instead of hand-setting the <model>_ids, why don’t you use the normal relationship saving methods?]
Yes, I am using DMZ, however… the Zones are it’s own table of UNIQUE Values, which is related to a Sport (also it’s own table of UNIQUE values).
And yes, Season is related to Zone, however, I am using a form with a drop-down of Zone Data when creating a new Season, hence the reason I’m setting the zone_id.
What would be the “normal” method in this case?
below are the other two Table Schemas:
CREATE TABLE IF NOT EXISTS `zones` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`sport_id` mediumint(9) NOT NULL,
`acro` varchar(10) NOT NULL,
`name` varchar(35) NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `acro` (`acro`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT AUTO_INCREMENT=4 ;
--
-- Dumping data for table `zones`
--
INSERT INTO `zones` (`id`, `sport_id`, `acro`, `name`, `created`, `updated`) VALUES
(1, 1, 'EGC', 'Elite Gridiron Champion', '2009-03-23 23:28:43', '2009-03-23 23:28:43'),
(2, 1, 'NGC', 'National Gridiron Challenge', '2009-03-23 23:28:43', '0000-00-00 00:00:00'),
(3, 1, 'FFL', 'Furious Football League', '2009-03-23 23:28:43', '0000-00-00 00:00:00');CREATE TABLE IF NOT EXISTS `sports` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`seq` smallint(5) unsigned NOT NULL,
`acro` varchar(10) NOT NULL,
`name` varchar(35) NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT AUTO_INCREMENT=2 ;
--
-- Dumping data for table `sports`
--
INSERT INTO `sports` (`id`, `seq`, `acro`, `name`, `created`, `updated`) VALUES
(1, 1, 'FTBL', 'Football', '2009-03-23 23:28:43', '2009-03-23 23:28:43');#695 / Apr 15, 2009 5:00pm
to add to the above.
You can have a Season with Duplicate Zone (zone_id), but offset by the Season # (seq).
#696 / Apr 15, 2009 5:05pm
One simple solution is to make your own validation rule. There are three steps:
First, add a method like _unique_zone_seq($field). This method can check to see that the two fields are unique. If they aren’t, return FALSE, otherwise return TRUE.
Second, add a line to a language file (you need to load this in, but you can do it in the rule itself) called ‘unique_zone_seq’.
Finally, add ‘unique_zone_seq’ to one or both of the fields.
$validation = array(
array('field' => 'seq', 'rules' => array('unique_zone_seq'))
);
...
function _unique_zone_seq($field) {
$unique = ... // query the DB for uniqueness
if(!$unique) {
$CI =& get_instance();
$CI->load->lang('season_errors'); // error message is in season_errors_lang.php
}
return $unique;
}See Validation in the docs for more explicit information.
update: There also is a unique_pair validation rule.
#697 / Apr 15, 2009 5:32pm
Thanks OverZealous!
I will try the “unique_pair” rule first.
As for the ‘hand-setting’ of the zone_id, since I’m getting the value from a form drop_down anyhow, is this still not the appropriate method?
#698 / Apr 15, 2009 5:49pm
So, in order to get self-referencing for say, a user, I would just go about doing it this way:
class User extends DataMapper {
var $has_many = array(
'user' => array(
'join_self_as' => 'user'
)
);
}Otherwise, I’m confused. I’m not sure if I’m allowed to do a normal relationship, or something else. I would use a complicated 3 table relationship to allow users to be related to eachother, but that seems silly and redundant. I also don’t know what the tables need to be named for the relationships in that situation.
Has anyone else tried this?
Thanks.
#699 / Apr 15, 2009 6:58pm
Thanks OverZealous!
I will try the “unique_pair” rule first.
As for the ‘hand-setting’ of the zone_id, since I’m getting the value from a form drop_down anyhow, is this still not the appropriate method?
Well, as per suggestion, “unique_pair” did the trick without need for custom validation routine (although I’m sure I will need that approach for another project).
Here is the updated model (Season).
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Season extends DataMapper {
var $table = 'seasons';
var $has_many = array('commissioner', 'registration', 'conference');
var $has_one = array('zone');
var $validation = array(
array(
'field' => 'zone_id',
'label' => 'Zone ID',
'rules' => array('required', 'trim', 'unique_pair' => 'seq')
),
array(
'field' => 'seq',
'label' => 'Season #',
'rules' => array('required', 'trim', 'unique_pair' => 'zone_id')
),
);
function Season()
{
parent::DataMapper();
}
function create($data=NULL)
{
$zone_id = $data['zone_id'];
$seq = $data['seq'];
$this->zone_id = $zone_id;
$this->seq = $seq;
$this->gametitle_id = $data['gametitle_id'];
if ( $this->save() )
return array('success' => TRUE, 'message' => "Record successfully created.");
else
{
$return_msg = $this->error->seq . 'Zone: ' . $zone_id . br() . ' Season #: ' . $seq;
return array('success' => FALSE, 'message' => $return_msg);
}
}
function update($data=NULL)
{
$zone_id = $data['zone_id'];
$seq = $data['seq'];
$s = $this->get_by_id($data['id']);
if ( $s->exists() )
{
$s->zone_id = $zone_id;
$s->seq = $seq;
$s->gametitle_id = $data['gametitle_id'];
if ( $s->save() )
return array('success' => TRUE, 'message' => "Record successfully updated.");
else
{
$return_msg = $this->error->seq . 'Zone: ' . $zone_id . br() . ' Season # ' . $seq;
return array('success' => FALSE, 'message' => $return_msg);
}
} else
{
$return_msg = $this->error->seq . 'Zone: ' . $zone_id . ' Season #: ' . $seq;
return array('success' => FALSE, 'message' => $return_msg);
}
}
}
/* End of file season.php */1) Was ‘unique_pair’ required on both columns of the validation?
2) Still awaiting the verdict on whether my “hand-setting” of the zone_id is still considered in-appropriate in this case.
And what the resolution would be. In the meantime, I will comb the forums and “docs” for what may be the cause of my ways.
#700 / Apr 15, 2009 8:02pm
@camporter1
You will always have to “name” both sides of the relationship. In other words, to have User related to User, you need to define a “parent” and “child” relationship. (Note: I’ve never tried to work with recursive self-references, meaning User1 is related to himself, so no guarantees that will work at all.)
class User extends DataMapper {
var $has_many = array(
'parent' => array(
'class' => 'user',
'other_field' => 'child'
),
'child' => array(
'class' => 'user',
'other_field' => 'parent'
)
);
}Then your table (in a many-to-many relationship) would be child_parent with the columns child_id and parent_id. You can also leave one side of the relationship as simply user, but that can sometimes be confusing.
Finally, to make changes, you’ll have to specify the field to save on when saving:
$user = ... // look up user
$parent = ... // look up parent
$user->save($parent, 'parent');
// or $user->save(array('parent' => $parent));
// or $parent->save($user, 'child');
// etc.@bEz
There’s nothing “wrong” with saving to the id’s directly, I guess I should have made that clear. It can even save a lot of time on the DB, if you trust the users.
However, there’s no validation being done on that id, so someone could send the server an invalid id, and really break your application, possibly even getting access to someone else’s data. I always recommend writing code as if every user was trying to break the server.
In other words, I would look up the related object by id, and then save that object using the normal $object->save($related). Of course, if you decide to do this, you’ll have to enable NULL values for the related columns, because DM doesn’t save relationships until after saving $object.
As for needing the rule on both, it probably isn’t necessary. The rule runs either way, and checking both just doubles the queries. The only drawback to running on just one is that you won’t get an error message on the other field.
#701 / Apr 16, 2009 8:25am
I am facing a very very strange issue—and this has just cropped up.
When I do a get_by_id and pass any id, the object always returns the same record.
Eg.
$id = 18;
$work->get_by_id($id);
echo $work->id;
the output is ALWAYS 16. No matter what I set $id as. I’m checking the value of $id just before I do the get, and I’ve tried where->get as well. Same issue.
Does anybody have any clue! HELP!
#702 / Apr 16, 2009 1:27pm
Try turning on CodeIgniter’s profiling, which should show you what queries are being run. At least you can try to see if the correct ID is getting to the query or not.
DataMapper does not have any known problems with get_by_id, so the bug might be either with custom code on your end, or your database. Also, make sure you aren’t accidentally overwriting any reserved names.
#703 / Apr 16, 2009 1:56pm
Thanks Phil, am checking the reserved names things immediately and will then go down the profiling route.
#704 / Apr 16, 2009 9:36pm
Well, Phil... the bad seems to come with the good, at least for now…
There’s nothing “wrong” with saving to the id’s directly, I guess I should have made that clear. It can even save a lot of time on the DB, if you trust the users.
However, there’s no validation being done on that id, so someone could send the server an invalid id, and really break your application, possibly even getting access to someone else’s data. I always recommend writing code as if every user was trying to break the server.
I do trust the users because:
1) They are managed by a user authorization script.
2) The available Zone IDs are provided by a dropdown.
3) However, I do and will maintain the notion that “every user is trying to break the server”
In other words, I would look up the related object by id, and then save that object using the normal $object->save($related). Of course, if you decide to do this, you’ll have to enable NULL values for the related columns, because DM doesn’t save relationships until after saving $object.
As for needing the rule on both, it probably isn’t necessary. The rule runs either way, and checking both just doubles the queries. The only drawback to running on just one is that you won’t get an error message on the other field.
I updated the object_id for Zone and Season tables to allow NULLS
I modified the create method of the season model by:
A) instantiating a new zone object
B) getting data via get_by_id(zone_id) passed in from form dropdown.
C) checking if the zone->exists()
The problem is that if I try and save that object using the normal $object->save($related)
$s->save($z)I can no longer use unique_pair validation on zone_id, because I would no longer be “DIRECTLY” saving the zone_id.
I had to remove the Validation portion for zone_id and the seq because without it being “directly set” it would fail to save.
Unfortunately, with it removed, if there was an attempt to duplicate a combination of the zone_id and the seq, it would save the season with a NULL zone_id. Also, I could not return any error strings when the save failed due to duplicate entry.
So basically, I did a mix of checking for zone existence, and if so, “directly saving” as before, as well as taking advantage of the unique_pair validation rule.
The problem I fair is that as I continue to develop, this will be a thorn in my side if your suggestions are possible and I did not resolved this.
For my scenario as it stands, i’m wondering if the functionality just isn’t coded into DMZ, or is somehow a bug.
Provided the current “working” model file. Hope you can assist with further troubleshooting.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Season extends DataMapper {
var $table = 'seasons';
var $has_many = array('commissioner', 'registration', 'conference');
var $has_one = array('zone');
var $validation = array(
array(
'field' => 'zone_id',
'label' => 'Zone',
'rules' => array('required', 'trim', 'unique_pair' => 'seq')
),
array(
'field' => 'seq',
'label' => 'Season #',
'rules' => array('required', 'trim', 'unique_pair' => 'zone_id')
),
);
function Season()
{
parent::DataMapper();
}
function create($data=NULL)
{
$zone_id = $data['zone_id'];
$seq = $data['seq'];
$z = new Zone();
$z->get_by_id($zone_id);
if ( $z->exists() )
{
$s = new Season();
$s->zone_id = $zone_id;
$s->seq = $seq;
$s->gametitle_id = $data['gametitle_id'];
if ( $s->save() )
return array('success' => TRUE, 'message' => "Record successfully created.");
else
{
$error_msg = $s->error->seq . 'Zone: ' . $zone_id . br() . ' Season: ' . $seq;
return array('success' => FALSE, 'message' => $error_msg);
}
} else
{
$error_msg = 'Cannot Save Record, Zone no longer exist!';
return array('success' => FALSE, 'message' => $error_msg);
}
}
}
/* End of file season.php */#705 / Apr 16, 2009 9:47pm
You have two options. Either continue with what you had been doing (and that’s fine, and it’s the fastest solution), or write a custom _related rule. There’s a section on that in the docs. I’m not sure you would be able to get that to work, though, because both fields wouldn’t be set at the same time.
So, I guess I would recommend using the previous method and sticking with the unique_pair.
I wonder if it would be worth adding “custom_field_validation” and “custom_related_validation” methods to DMZ that runs after all of the other rules are run, for complicated validation that needs to happen after everything else has been checked, but before saving or committing a transaction.