Ticket #1131 (closed defect: fixed)

Opened 13 months ago

Last modified 5 months ago

$record->get('foo_id') returns related record 'Foo' instead of field value

Reported by: mahono Owned by: romanb
Priority: major Milestone: 2.0.0 (OLD)
Component: Record Version: 1.0.0
Severity: Keywords: get related record
Cc: Has Test: yes
Status: Pending Core Response Has Patch: no

Description

Longer version: I have a record with this (shortened) table definition:

setTableDefinition(): $this->hasColumn('foo_id', 'integer');
setUp(): $this->hasOne('Foo', array('local' => 'foo_id', 'foreign' => 'id'));
  • BEFORE I access $record->Foo, $record->get('foo_id') works fine (returns the assigned integer).
  • AFTER I access $record->Foo, $record->get('foo_id') returns $this->Foo.

So for me it is a bug, isn't it?

Attachments

1131Record.diff (1.4 KB) - added by jasoneisen 9 months ago.

Change History

  Changed 13 months ago by romanb

I agree with you ... however this seems to be expected behavior currently ... Take a look here:  http://trac.phpdoctrine.org/browser/branches/0.11/lib/Doctrine/Relation/LocalKey.php

in fetchRelatedFor you find this explicit statement: $record->set($localFieldName, $related, false);

I agree with you that this behavior is "wrong", however i'm not sure we can change it prior to 1.0 ... ? What does everyone else think about this?

  Changed 13 months ago by mahono

It is really an odd behaviour as the expected result changes depending on whether the related record is loaded or not. In my case I need to add an ugly workaround just because the type of result is not reliable.

get('foo_id') must ALWAYS return the value from the column and never the related record. Therefore we have the relation name 'Foo'. That is a clear separation and it should work exactly that way.

IMHO ASAP :-)

  Changed 13 months ago by tuct

i think we should changes this asap get('foo_id') has to reurn the value not the object (imho)

  Changed 13 months ago by Max_Schreck

This issue is also pointed out in a  test case for  ticket 1072.

I think it's wrong too. Because it's not consistent. It depends if I access the relation or not and thus I can have different results. For example in testing this returned value because object (even empty relation) is always TRUE... and this could have a serious consequences.

BTW: Testing a relation is still a problem for me. It could be fine to have some guaranteed way for doing this. Because if I try to find out if a record has some to-one relation then this relation gets created and if I try to save the record later - it can, depending on the schema, complain about integrity constraint violation (better case) or save the newly created relation (worse case).

  Changed 13 months ago by romanb

  • milestone changed from 0.11.3 to 0.12.0

We have decided to make this change in 0.12 which is due in about 4 weeks. We cant make this change earlier as it breaks backwards compatibility and we're only a few days away from a major release (0.11).

follow-up: ↓ 7   Changed 13 months ago by romanb

Max_Schreck: isset($foo->Relation) ? It does not trigger a lazy-load of course. So its FALSE even if the related object exists (in the db) but is not fetched.

in reply to: ↑ 6   Changed 13 months ago by Max_Schreck

Replying to romanb:

Another way is checking only a relation column and if it's ok then access the relation object but this policy must be followed in a whole application. Again it's only a partial solution... not so simple and reliable as for to-many relations which are easily detectable by count() (and without side effects). It's just a suggestion. I love Doctrine but in more complex web applications this gives me sometimes a headache. :)

  Changed 11 months ago by chorizo

I applied this patch to work around this issue:

Index: lib/Doctrine-0.11.1/lib/Doctrine/Record.php
===================================================================
--- lib/Doctrine-0.11.1/lib/Doctrine/Record.php (revision 7275)
+++ lib/Doctrine-0.11.1/lib/Doctrine/Record.php (working copy)
@@ -1017,7 +1017,16 @@
                         throw new Doctrine_Record_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Record or Doctrine_Null when setting one-to-one references.");
                     }
                     if ($rel instanceof Doctrine_Relation_LocalKey) {
-                        if ( ! empty($foreignFieldName) && $foreignFieldName != $value->getTable()->getIdentifier()) {
+                        $identifier = $value->getTable()->getIdentifier();
+                        if (is_array($identifier) && count($identifier) == 1) {
+                          $identifier = array_shift($identifier);
+                        }
+
+                        if (empty($foreignFieldName)) {
+                          $foreignFieldName = $identifier;
+                        }
+
+                        if ( !is_array($identifier) && $value->get($foreignFieldName, false) != null) { // TODO: this is the line that seems backwards
                             $this->set($localFieldName, $value->rawGet($foreignFieldName), false);
                         } else {
                             $this->set($localFieldName, $value, false);

It doesn't solve the problem, because objects without Identifiers are still stored in the column, but fetching existing relations don't cause the problem.

  Changed 11 months ago by jwage

  • version changed from 0.11 to 1.0
  • milestone changed from Unknown to 1.0.0-RC1

  Changed 11 months ago by romanb

  • status changed from new to assigned

  Changed 11 months ago by guilhermeblanco

  • status changed from assigned to closed
  • resolution set to worksforme

I tried to reproduce this ticket in many ways, without success.

I'm marking this ticket as "works for me", since I added coverage to your issue and could not replicate. If you are still experiencing the issue, reopen the ticket and please provide a failing test case.

Coverage about your issue was added in r4780.

  Changed 10 months ago by chorizo

  • status changed from closed to reopened
  • resolution worksforme deleted
  • has_test set

Here is a test case that I believe shows this problem:

  function testRelationAccessChangesColumnValueToObject() {                                                                                                                                                             
    $phone = new Phonenumber();                                                                                                                                                                          
    $phone->entity_id = null;                                                                                                                                                                            
    $phone->phonenumber = '555-1212';                                                                                                                                                                    
    $phone->save();                                                                                                                                                                                      
    $this->assertNull($phone->entity_id);                                                                                                                                                                
    $phone->Entity;                                                                                                                                                                                      
    $this->assertNull($phone->entity_id);                                                                                                                                                                
  }     

If you comment out the "$phone->Entity" line, the second assertion passes. As written, the $phone->entity_id returns an empty Entity object. It is unexpected that a column should ever return a Doctrine object, and coding around this bug isn't always easy.

  Changed 10 months ago by anonymous

  • milestone New deleted

Milestone New deleted

  Changed 10 months ago by jasoneisen

Reproduced in a different manner with coverage in r4900

  Changed 10 months ago by jasoneisen

Updated the test case again with r4902.

In testTicketWithOverloadingAndTwoQueries(), without the first query it passes, and adding ->leftJoin('g.Role gr') to the second query it passes.

  Changed 10 months ago by jwage

  • milestone set to 2.0.0

  Changed 10 months ago by purnama

same for me, should be a bug.

this patch is work for me:

    public function fetchRelatedFor(Doctrine_Record $record)
    {
        $def = $this->definition;
        $a = 1;
        $localFieldName = $record->getTable()->getFieldName($this->definition['local']);
        $id = $record->get($localFieldName);

        if (empty($id) || ! $this->definition['table']->getAttribute(Doctrine::ATTR_LOAD_REFERENCES)) {
            $related = $this->getTable()->create();
        } else {
            $dql  = 'FROM ' . $this->getTable()->getComponentName()
                 . ' WHERE ' . $this->getCondition();
            
            $related = $this->getTable()
                            ->getConnection()
                            ->query($dql, array($id))
                            ->getFirst();
      
            if ( ! $related || empty($related)) {
                $related = $this->getTable()->create();
            }
        }       
      
-       $record->set($localFieldName, $related, false);
+       $record->set($localFieldName, $related->get($this->definition['foreign']), false);

        return $related;
    }

Changed 9 months ago by jasoneisen

  Changed 9 months ago by jasoneisen

I added what i had narrowed it down to in the attachment 1131Record.diff

  Changed 5 months ago by guilhermeblanco

  • status changed from reopened to closed
  • resolution set to fixed

(In [5437]) [1.1] Fixed #1131. Now reference columns to not store the related objects on it. Thanks German for patch.

Note: See TracTickets for help on using tickets.