Account Options

  1. Sign in
The old Google Groups will be going away soon.
Switch to the new Google Groups.
Google Groups Home
« Groups Home
Awkward handling of One-to-Zero-or-One relationships in Doctrine (Correction)
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  4 messages - Collapse all  -  Translate all to Translated (View all originals)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Max.Zalota  
View profile  
 More options Mar 31 2008, 2:16 pm
From: "Max.Zalota" <mzal...@yahoo.com>
Date: Mon, 31 Mar 2008 11:16:50 -0700 (PDT)
Local: Mon, Mar 31 2008 2:16 pm
Subject: Awkward handling of One-to-Zero-or-One relationships in Doctrine (Correction)
Sorry...My previous posting had an error in the subject line.

Unfortunately I discovered that Doctrine does not handle one-to-zero-
or-one relationships well. In fact Doctrine assumes that one-to-one
relationships cannot be one-to-zero-or-one but Doctrine's
documentation does not explicitly say so (e.g. in chapter "4.2.1 One-
To-One" or in chapter "7.1 Dealing with relations").
Programmers who expect Doctrine to treat one-to-zero-or-one
relationships correctly are bound to discover unexpected behavior.

Before I go any further let me premise this posting by saying that
overall I am very happy with Doctrine. Our company chose it as the
default ORM solution for our Enterprise-Wide applications platform.

Here is an illustration of the problem:
Imagine an object that represents a car Insurance Policy with its own
ID, date, status, etc. This Policy object has one-to-one (1-1)
"Applicant" relationship with a Person object. (Person object has its
own ID, FirstName, LastName, etc.)
A Policy object also has a one-to-zero-or-one (1-0..1) "Co-applicant"
relationship with another Person object.

class OrmPolicies extends BaseOrmPolicies
{
    public function setUp()
    {
        // notice the 'as' keyword here
        $this->hasOne('OrmPersons as Applicant', array( 'local' =>
'applicant_id',
                                                        'foreign' =>
'id'));

        $this->hasOne('OrmPersons as CoApplicant', array('local' =>
'coapplicant_id',
                                                        'foreign' =>
'id'));
   }

}

Now consider a script that displays a policy and then updates its
status:

line 01: $id = 15; //just some ID in Policy table
line 02: $policyObj = $conn->getTable('OrmPolicies ')->find($id);
line 03: $applicantObj = $policyObj->Applicant;
line 04: $coapplicantObj = $policyObj->CoApplicant;
line 05:
line 06: //Print out information about Applicant and coapplicant
line 07: echo "Applicant: ".$applicantObj->FirstName." ".$applicantObj-

>LastName."\n";

line 08: if ($coapplicantObj->exists()){
line 09:   echo "Co-Applicant: ".$coapplicantObj ->FirstName." ".
$coapplicantObj ->LastName."\n";
line 10: }
line 11:
line 12: //Set status to a new value.
line 13: $policyObj->status = "Closed";
line 14: $policyObj->save();

If at the beginning of the script a policy did not have a coapplicant
defined (coapplicant_id was NULL in Policy table) then at the end of
the script Doctrine will add a new empty applicant to the policy
number!!!
It is NOT OBVIOUS at all to the programmer that this script might have
such consequences.

In line 04 Doctrine will create a new Person object with all fields
set to default values and linked to the $policyObject. In line 14
Doctrine will then insert the new Person object into the database and
update Policy's coapplicant_id foreign key to point to the new Person
object.

Unfortunately I could not come up with any elegant workarounds for
this problem. I had to remove CoApplicant relationship from Policy's
setUp function and manually resolve applicant_ids everywhere (see
lines 04 and 09 in the code below)

class OrmPolicies extends BaseOrmPolicies
{
    public function setUp()
    {
        // notice the 'as' keyword here
        $this->hasOne('OrmPersons as Applicant', array( 'local' =>
'applicant_id',
                                                        'foreign' =>
'id'));

}

line 01: $id = 15; //just some ID in Policy table
line 02: $policyObj = $conn->getTable('OrmPolicy')->find($id);
line 03: $applicantObj = $policyObj->Applicant;
line 04: $coapplicant_id = $policyObj->coapplicant_id
line 05:
line 06: //Print out information about Applicant and coapplicant
line 07: echo "Applicant: ".$applicantObj->FirstName." ".$applicantObj-
>LastName."\n";

line 08: if (coapplicant_id<>NULL){
line 09:   $coapplicantObj = $conn->getTable('OrmPerson')-
>find(coapplicant_id);

line 10:   echo "Co-Applicant: ".$coapplicantObj ->FirstName." ".
$coapplicantObj ->LastName."\n";
line 11: }
line 12:
line 13: //Set status to a new value.
line 14: $policyObj->status = "Closed";
line 15: $policyObj->save();

The culprit of this behavior is the assumption that is made in
Doctrine_Relation_ForeignKey::fetchRelatedFor() function. (line 58 in
Relation/ForeignKey.php file in Doctrine-0.10.4 release). The critical
code look as follows
       ...
        if ($this->isOneToOne()) {
            if ( ! $record->exists() || empty($id) ||
                 ! $this->definition['table']-

>getAttribute(Doctrine::ATTR_LOAD_REFERENCES)) {

                $related = $this->getTable()->create();
            } else {
                $dql  = 'FROM ' . $this->getTable()-
>getComponentName()

                      . ' WHERE ' . $this->getCondition();

                $coll = $this->getTable()->getConnection()-

>query($dql, $id);

                $related = $coll[0];
            }

            $related->set($related->getTable()->getFieldName($this-

>definition['foreign']), $record, false);

        } else {...

In the fifth line of this code excerpt you see: $related = $this-

>getTable()->create(); which later followed by

$related->set($related->getTable()->getFieldName($this-

>definition['foreign']), $record, false);

In order to handle one-to-zero-or-one relationship I would expect that
no new object be created (or Doctrine_Null object should be created
instead).

I would appreciate any comments/suggestions about more elegant
workarounds and any possible solutions to this problem in the future.

Best Regards,

Max Zalota


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Jonathan Wage  
View profile  
 More options Mar 31 2008, 3:46 pm
From: "Jonathan Wage" <jonw...@gmail.com>
Date: Mon, 31 Mar 2008 14:46:22 -0500
Local: Mon, Mar 31 2008 3:46 pm
Subject: Re: [doctrine-user] Awkward handling of One-to-Zero-or-One relationships in Doctrine (Correction)

When you call a relationship from another object, it automatically
instantiates it and associates it to the object, so when you call save(), it
will be saved and foreign keys set. If you wish to check if a relationship
exists, use isset() to check if it exists first, or you can check if the
foreign key id is set, that would tell you if the relationship exists or
not.

- Jon

--
Jonathan Wage
http://www.jwage.com
http://www.centresource.com

 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
romanb  
View profile  
 More options Mar 31 2008, 5:33 pm
From: romanb <ro...@code-factory.org>
Date: Mon, 31 Mar 2008 14:33:23 -0700 (PDT)
Local: Mon, Mar 31 2008 5:33 pm
Subject: Re: Awkward handling of One-to-Zero-or-One relationships in Doctrine (Correction)
To be honest, i think this behavior is really not good. The Collection
class does the same, instantiating new objects on the fly when you try
to get an object out of it that doesnt exist. I know it's *needed* to
support some syntactical sugar but that's just too much magic and
quickly leads to weird behavior. I already had quite a few similar
situations already. We might want to reconsider this behavior in the
future.

On 31 Mrz., 21:46, "Jonathan Wage" <jonw...@gmail.com> wrote:


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Adam Huttler  
View profile  
 More options Mar 31 2008, 5:46 pm
From: "Adam Huttler" <adam.hutt...@fracturedatlas.org>
Date: Mon, 31 Mar 2008 17:46:13 -0400 (EDT)
Local: Mon, Mar 31 2008 5:46 pm
Subject: RE: [doctrine-user] Re: Awkward handling of One-to-Zero-or-One relationships in Doctrine (Correction)

I agree completely. This has tripped me up more than once. It actually
interferes with the syntactic sugar, since you can't use it much of the
time without having unintended side effects. Is there a way to have some
more sophisticated logic here? Like perhaps the related object isn't
actually attached until one of it's properties is set or a method
(instantiate(), attach() ?) is called. Not sure how feasible that would be
to code, though...

Having said all of that, Jon's isset() workaround is pretty reasonable and
allows you to keep the syntactic sugar.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
End of messages
« Back to Discussions « Newer topic     Older topic »