One to one

Join one entity to another.

A one-to-one relationship involves a single parent entity which has a property pointing to a single child entity. The child entity can have a property pointing back to the parent (ie. is "bidirectional"), but it doesn't have to.

If the child points back to the parent, you can effectively think of the child entity as being the parent of that one-to-one relationship - so both the parent and the child have a one-to-one relationship with the other entity. Thus, you don't really have to worry about whether a relationship is unidirectional or bidirectional, as they are treated the same way.

What you do have to worry about though, is which side owns the relationship. This is easy to figure out, because the owning entity is stored in the database table which has the foreign key column. For example, suppose we have an entity called Customer and another entity called MarketingPreferences. The code for these entities looks like this:

class Customer
{
    public int $id;
    public string $name;
    public MarketingPreferences $preferences;
}

// Note: Each class would of course normally be
// in a separate file.

class MarketingPreferences
{
    public int $id;
    public Customer $customer;
    public bool $allowEmail;
    public bool $allowTextMessage;
}

We store the values of the properties for these entities in two database tables, named customer and marketing_preferences:

The marketing_preferences table contains a foreign key column named customer_id which holds the primary key value of the customer table. Note that it is not necessary (nor would it be desirable) to store the id of the marketing_preferences table in the customer table as well (although we could do that instead of having customer_id in the marketing_preferences table).

So, in the above example, because the marketing_preferences table holds the foreign key that defines the one-to-one relationship, the MarketingPreferences entity owns the relationship. This means that the mapping between the database column and the property will need to go on the MarketingPreferences entity, not the Customer entity. Even so, on the Customer entity, we still need to tell Objectiphy where to find the mapping information, hence we use the mappedBy attribute in the mapping definition, as shown below.

Examples

Here are two example of how to set up the mapping for a one-to-one relationship between two entities that point to each other, using attributes or annotations (these examples are all equivalent; you only need to use one type of mapping provider, it is up to you which one).

Note that the sourceJoinColumn attribute (or @ORM\JoinColumn annotation if using Doctrine annotations) exists on the owning side (in this case the 'child' object - MarketingPreferences), and the other side (in this case, the 'parent', Customer) has a mappedBy attribute, telling Objectiphy where to look for the join column. The value of mappedBy must be a property name, not a column name!

Example 1: Minimal Mapping

The following example shows the bare minimum mapping information necessary, on the assumption that the foreign key in the owning table is the primary key of the owned table and that a left join is sufficient. If you are using non-standard columns, or require an inner join, you will need to define them in full as shown in example 2 (note, if using Doctrine annotations, you cannot specify the join type with an annotation).

use Objectiphy\Objectiphy\Table;
use Objectiphy\Objectiphy\Column;
use Objectiphy\Objectiphy\Relationship;

#[Table(name: 'customer')]
class Customer
{
    #[Column(isPrimaryKey: true)]
    public int $id;
    
    #[Column]
    public string $name;
    
    #[Relationship(
        relationshipType: 'one_to_one',
        childClassName: MarketingPreferences::class,
        mappedBy: 'customer'
    )]
    public MarketingPreferences $preferences;
}

// Note: Each class would of course normally be
// in a separate file.

#[Table(name: 'marketing_preferences')]
class MarketingPreferences
{
    #[Column(isPrimaryKey: true)]
    public int $id;
  
    #[Relationship(
        relationshipType: 'one_to_one',
        childClassName: Customer::class
    )]        
    public Customer $customer;
    
    #[Column]
    public bool $allowEmail;
    
    #[Column]
    public bool $allowTextMessage;
}

Example 2: Full Mapping

The following example shows all of the mapping information defined, even though some items could be omitted as Objectiphy can guess some of the mapping if it follows standard conventions. You only need to specify all of the mapping information if you are not joining in a standard way from foreign key to primary key, if your foreign key column name is not suffixed with 'id', or the primary key of the target table, or if you need an inner join (note, if using Doctrine annotations, you cannot specify the join type with an annotation). Example 1, above, shows the exact same mapping as this in an abbreviated format.

use Objectiphy\Objectiphy\Table;
use Objectiphy\Objectiphy\Column;
use Objectiphy\Objectiphy\Relationship;

#[Table(name: 'customer')]
class Customer
{
    #[Column(isPrimaryKey: true)]
    public int $id;
    
    #[Column] 
    public string $name;
    
    #[Relationship(
        relationshipType: 'one_to_one',
        childClassName: MarketingPreferences::class,
        mappedBy: 'customer'
    )]
    public MarketingPreferences $preferences;
}

// Note: Each class would of course normally be
// in a separate file.

#[Table(name: 'marketing_preferences')]
class MarketingPreferences
{
    #[Column(isPrimaryKey: true)] 
    public int $id;
    
    #[Relationship(
        relationshipType: 'one_to_one',
        childClassName: Customer::class,
        sourceJoinColumn: 'customer_id',
        targetJoinColumn: 'id',
        joinType: 'LEFT'
    )]
    public Customer $customer;
    
    #[Column]
    public bool $allowEmail;
    
    #[Column]
    public bool $allowTextMessage;
}

Last updated