18/02/2017 - DOCTRINE, SYMFONY
Bu yazıda cascade={"remove"}
ve orphanRemoval=true
doctrine özelliklerinin bidirectional one-to-one ilişkilerde nasıl kullanılacağını öğreneceğiz. Bidirectional ilişki kullanmamızın nedeni, her iki entity üzerinden her iki tarafa doğru gidebilme olanağımızın olmasıdır. Örneğimizin teması futbol olacak.
Eğer cascade={"remove"}
veya orphanRemoval=true
özelliğinin ayarlı olduğu entityden bir kayıt silerseniz, bu işlem diğer ilişkisel entitylerden de otomatik olarak kayıtları silmeyi deneyecektir. Hem cascade={"remove"}
hem de orphanRemoval=true
özelliklerini entity ilişkilerinde ayarlarken çok dikkatli olmanız gerekir çünkü, yanlışlıkla arka planda bilginiz dışında diğer entity kayıtlarınıda silebilirsiniz.
inversedBy
özelliğini kullanır.mappedBy
özelliğini kullanır.nullable=false
özelliği composition ilişkisini yaratır. Eğer bir tane League kaydı yaratmak isterseniz, bir tane de Country kaydı yaratmanız gerekir.class Country
{
/**
* @ORM\OneToOne(targetEntity="League", mappedBy="country")
*/
private $league;
}
class League
{
/**
* @ORM\OneToOne(targetEntity="Country", inversedBy="league")
* @ORM\JoinColumn(name="country_id", referencedColumnName="id", nullable=false)
*/
private $country;
}
`country` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
)
`league` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`country_id` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_3EB4C318F92F3E70` (`country_id`),
CONSTRAINT `FK_3EB4C318F92F3E70` FOREIGN KEY (`country_id`) REFERENCES `country` (`id`)
)
mysql> SELECT
-> `country`.`id` AS CountryID,
-> `country`.`name` AS CountryName,
-> `league`.`id` AS LeagueID,
-> `league`.`name` AS LeagueName
-> FROM `country`
-> INNER JOIN `league` ON `country`.`id` = `league`.`country_id`;
+-----------+-------------+----------+----------------+
| CountryID | CountryName | LeagueID | LeagueName |
+-----------+-------------+----------+----------------+
| 1 | England | 1 | Premier League |
+-----------+-------------+----------+----------------+
1 rows in set (0.00 sec)
class Country
{
/**
* @ORM\OneToOne(targetEntity="League", mappedBy="country")
*/
private $league;
}
class League
{
/**
* @ORM\OneToOne(targetEntity="Country", inversedBy="league")
* @ORM\JoinColumn(name="country_id", referencedColumnName="id", nullable=false)
*/
private $country;
}
Country silme: League'in varlığı Country'nin varlığına bağlı olduğu için "Integrity constraint violation" hatası oluşur.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM country ... LEFT JOIN league ...
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM country WHERE id = ? [1] []
doctrine.DEBUG: "ROLLBACK" [] []
League silme: League silinir ama Country olduğu gibi kalır.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM league ...
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
Eğer cascade={"remove"}
yerine orphanRemoval=true
kullanmış olsaydınız, sonuç değişmezdi.
class Country
{
/**
* @ORM\OneToOne(targetEntity="League", mappedBy="country", cascade={"remove"})
*/
private $league;
}
class League
{
/**
* @ORM\OneToOne(targetEntity="Country", inversedBy="league")
* @ORM\JoinColumn(name="country_id", referencedColumnName="id", nullable=false)
*/
private $country;
}
Country silme: Country ve League silinir.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM country ... LEFT JOIN league ...
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: DELETE FROM country WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
League silme: League silinir ama Country olduğu gibi kalır.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM league ...
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
Eğer cascade={"remove"}
yerine orphanRemoval=true
kullanmış olsaydınız, sonuç değişmezdi.
class Country
{
/**
* @ORM\OneToOne(targetEntity="League", mappedBy="country")
*/
private $league;
}
class League
{
/**
* @ORM\OneToOne(targetEntity="Country", inversedBy="league", cascade={"remove"})
* @ORM\JoinColumn(name="country_id", referencedColumnName="id", nullable=false)
*/
private $country;
}
Country silme: League'in varlığı Country'nin varlığına bağlı olduğu için "Integrity constraint violation" hatası oluşur.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM country ... LEFT JOIN league ...
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM country WHERE id = ? [1] []
doctrine.DEBUG: "ROLLBACK" [] []
League silme: League ve Country silinir.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM league ...
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: DELETE FROM country WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
Eğer cascade={"remove"}
yerine orphanRemoval=true
kullanmış olsaydınız, sonuç değişmezdi.
class Country
{
/**
* @ORM\OneToOne(targetEntity="League", mappedBy="country", cascade={"remove"})
*/
private $league;
}
class League
{
/**
* @ORM\OneToOne(targetEntity="Country", inversedBy="league", cascade={"remove"})
* @ORM\JoinColumn(name="country_id", referencedColumnName="id", nullable=false)
*/
private $country;
}
Country silme: Country ve League silinir.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM country ... LEFT JOIN league ...
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: DELETE FROM country WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
League silme: League ve Country silinir.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM league ...
doctrine.DEBUG: SELECT ... FROM country ... LEFT JOIN league ...
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: DELETE FROM country WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
Bu örnekte Country bağımsız bir entitydir yani League'ye ne olursa olsun, arka planda Country'nin otomatik olarak silinmemesi gerekir. Bununla birlikte League'in varlığı Country'nin varlığına bağlı olduğu için, eğer Country silinirse League'de arka planda otomatik olarak silinmelidir. Bu mantığa bakıldığında, elimizdeki en uygun seçenek Cascade 2 olur yani, cascade={"remove"}
veya orphanRemoval=true
Country entity üzerinde ayarlı olacaktır.