phpstan-doctrine
phpstan-doctrine copied to clipboard
Cannot satisfy not nullable OneToOne inverse side
I cannot find a way to satisfy the case
Property App\User::$stats type mapping mismatch: database can contain App\UserStats|null but property expects App\UserStats.
I have the following mapping :
<?php
class User
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
protected $id;
/**
* @var UserStats
*
* @ORM\OneToOne(targetEntity="App\UserStats", mappedBy="user")
*/
protected $stats;
// ... other properties
}
class UserStats
{
/**
* @var User
*
* @ORM\Id
* @ORM\OneToOne(targetEntity="App\User", inversedBy="stats")
*/
private $user;
// ... other properties
}
If I add @ORM\JoinColumn(nullable=false) on User::$stats property then Doctrine is broken because JoinColumn annotation is expected on the owning side of the one to one relationship.
So I'm stuck with this case, because I can't explain to phpstan that my property isn't nullable :/
I'm having the exact same problem. Did you find a workaround (other than just ignoring those errors)?
No I didn't find a good solution. I gived up and add it to the ignored errors :/
I will try to look into it.
Thanks for replying @noemi-salaun I've ended up doing the same. Also thanks @lookyman - really appreciate it 😄
I have a similar issue, except my use-case is the opposite - I have a non-nullable database field, but need the property itself to be nullable for creation (and then use a NotNull assert to prevent possible issues).
@noemi-salaun I have the same, but with an additional @ORM\JoinColumn(nullable=false) on UserStats::$user. This tells Doctrine that there can be no UserStats with an empty user column. This aspect might already be included in @ORM\Id though ;-)
I think the real problem is: From looking at the mapping, you cannot answer the following question:
When a
Useris created, will there be aUserStatscreated in any case?
...cause the mapping does allow a User without a UserStats. In my app, I can answer with YES, cause in Users constructor I have:
$this->setStats(new UserStats($this));
... and cascade={"persist"}) on User::$stats.
@lookyman Checking for those two aspects might be an idea to resolve this.
Doctrine docs: https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/tutorials/composite-primary-keys.html#use-case-2-simple-derived-identity
I have a similar problem
class Order
{
/**
* @var int
*
* @ORM\Id()
* @ORM\GeneratedValue(strategy="IDENTITY")
* @ORM\Column(name="id", type="integer", nullable=false)
*/
private $id;
/**
* @var Billing
*
* @ORM\OneToOne(targetEntity="Billing", mappedBy="order", orphanRemoval=true, cascade={"persist"})
*/
private $billing;
// ...
}
class Billing
{
/**
* @var int
*
* @ORM\Id()
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var Order
*
* @ORM\OneToOne(targetEntity="Order", inversedBy="billing")
* @ORM\JoinColumn(name="order_id", referencedColumnName="id", nullable=false)
*/
private $order;
/**
* @var int
*
* @ORM\Column(name="order_id", type="integer", nullable=false)
*/
private $orderId;
// ....
}
PHPStan error message:
Property Order::$billing type mapping mismatch: database can contain Billing|null but property expects Billing
@mitelg If there's no Billing for order ID 123 in the database, the $billing property for Order 123 will be null.
@ondrejmirtes thanks for your reply
but an order without billing is not valid and should/could not happen 🤔 at least, I hope so 😅🙈
is there a way to ensure that?
I would have to look it up tomorrow, but on DB level, this should be prevented.
I think it can't be enforced on the DB level.
hey @ondrejmirtes
I had a deeper look in the code, and the order and the billing are created directly via SQL :see_no_evil: So due to erroneous behaviour at some point, it could indeed be possible that an order has no billing in the respective doctrine model :grimacing:
legacy software is fun... :tada:
thank you very much again!