18/02/2017 - DOCTRINE, SYMFONY
Bu yazıda cascade={"remove"}
ve orphanRemoval=true
doctrine özelliklerinin bidirectional one-to-many 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 ilişkinin ManyToOne tarafında kullanılır.mappedBy
özelliğini ilişkinin OneToMany tarafında kullanılır.nullable=false
özelliği composition ilişkisini yaratır. Eğer bir tane Team kaydı yaratmak isterseniz, bir tane de League kaydı yaratmanız gerekir.class League
{
/**
* @ORM\OneToMany(targetEntity="Team", mappedBy="league")
*/
private $teams;
public function __construct()
{
$this->teams = new ArrayCollection();
}
}
class Team
{
/**
* @ORM\ManyToOne(targetEntity="League", inversedBy="teams")
* @ORM\JoinColumn(name="league_id", referencedColumnName="id", nullable=false)
*/
private $league;
}
`league` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
)
`team` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`league_id` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_C4E0A61F58AFC4DE` (`league_id`),
CONSTRAINT `FK_C4E0A61F58AFC4DE` FOREIGN KEY (`league_id`) REFERENCES `league` (`id`)
)
mysql> SELECT
-> `league`.`id` AS LeagueID,
-> `league`.`name` AS LeagueName,
-> `team`.`id` AS TeamID,
-> `team`.`name` AS TeamName
-> FROM `league`
-> INNER JOIN `team` ON `league`.`id` = `team`.`league_id`
-> WHERE
-> `team`.`league_id` = 1;
+----------+------------+--------+-------------+
| LeagueID | LeagueName | TeamID | TeamName |
+----------+------------+--------+-------------+
| 1 | Super Lig | 1 | Fenerbahce |
| 1 | Super Lig | 2 | Galatasaray |
| 1 | Super Lig | 3 | Besiktas |
+----------+------------+--------+-------------+
3 rows in set (0.00 sec)
class League
{
/**
* @ORM\OneToMany(targetEntity="Team", mappedBy="league")
*/
private $teams;
public function __construct()
{
$this->teams = new ArrayCollection();
}
}
class Team
{
/**
* @ORM\ManyToOne(targetEntity="League", inversedBy="teams")
* @ORM\JoinColumn(name="league_id", referencedColumnName="id", nullable=false)
*/
private $league;
}
League silme: Team'in varlığı League'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 league WHERE id = ? LIMIT 1 ["1"] []
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: "ROLLBACK" [] []
Team silme: Team silinir ama League olduğu gibi kalır.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM team WHERE id = ? LIMIT 1 ["1"] []
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
Eğer cascade={"remove"}
yerine orphanRemoval=true
kullanmış olsaydınız, sonuç değişmezdi.
class League
{
/**
* @ORM\OneToMany(targetEntity="Team", mappedBy="league", cascade={"remove"})
*/
private $teams;
public function __construct()
{
$this->teams = new ArrayCollection();
}
}
class Team
{
/**
* @ORM\ManyToOne(targetEntity="League", inversedBy="teams")
* @ORM\JoinColumn(name="league_id", referencedColumnName="id", nullable=false)
*/
private $league;
}
League silme: League ve Team silinir.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM league WHERE id = ? LIMIT 1 ["1"] []
doctrine.DEBUG: SELECT ... FROM team WHERE league_id = ? [1] []
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [1] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [2] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [3] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
Team silme: Team silinir ama League olduğu gibi kalır.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM team WHERE id = ? LIMIT 1 ["1"] []
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
Eğer cascade={"remove"}
yerine orphanRemoval=true
kullanmış olsaydınız [Creation Error] The annotation @ORM\ManyToOne declared on property Team::$league does not have a property named "orphanRemoval". Available properties: targetEntity, cascade, fetch, inversedBy
hatası alırdınız.
class League
{
/**
* @ORM\OneToMany(targetEntity="Team", mappedBy="league")
*/
private $teams;
public function __construct()
{
$this->teams = new ArrayCollection();
}
}
class Team
{
/**
* @ORM\ManyToOne(targetEntity="League", inversedBy="teams", cascade={"remove"})
* @ORM\JoinColumn(name="league_id", referencedColumnName="id", nullable=false)
*/
private $league;
}
League silme: Team'in varlığı League'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 league WHERE id = ? LIMIT 1 ["1"] []
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: "ROLLBACK" [] []
Team silme: Tüm Team'lerin varlığı League'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 team WHERE id = ? LIMIT 1 ["1"] []
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [1] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: "ROLLBACK" [] []
Eğer cascade={"remove"}
yerine [Creation Error] The annotation @ORM\ManyToOne declared on property Team::$league does not have a property named "orphanRemoval". Available properties: targetEntity, cascade, fetch, inversedBy
hatası alırdınız.
class League
{
/**
* @ORM\OneToMany(targetEntity="Team", mappedBy="league", cascade={"remove"})
*/
private $teams;
public function __construct()
{
$this->teams = new ArrayCollection();
}
}
class Team
{
/**
* @ORM\ManyToOne(targetEntity="League", inversedBy="teams", cascade={"remove"})
* @ORM\JoinColumn(name="league_id", referencedColumnName="id", nullable=false)
*/
private $league;
}
League silme: League ve Team silinir.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM league WHERE id = ? LIMIT 1 ["1"] []
doctrine.DEBUG: SELECT ... FROM team WHERE league_id = ? [1] []
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [1] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [2] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [3] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
Team silme: Team ve League silinir.
$this->entityManager->remove($entity);
$this->entityManager->flush();
doctrine.DEBUG: SELECT ... FROM team WHERE id = ? LIMIT 1 ["1"] []
doctrine.DEBUG: SELECT ... FROM league WHERE id = ? [1] []
doctrine.DEBUG: SELECT ... FROM team WHERE league_id = ? [1] []
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [2] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [3] []
doctrine.DEBUG: DELETE FROM team WHERE id = ? [1] []
doctrine.DEBUG: DELETE FROM league WHERE id = ? [1] []
doctrine.DEBUG: "COMMIT" [] []
Bu örnekte League bağımsız bir entitydir yani Team'e ne olursa olsun, arka planda League'nin otomatik olarak silinmemesi gerekir. Bununla birlikte Team'in varlığı League'nin varlığına bağlı olduğu için, eğer League silinirse Team'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
League entity üzerinde ayarlı olacaktır.