【发布时间】:2014-03-19 15:59:18
【问题描述】:
我想检查用户是否更改了编辑表单中的Document图像。如果用户更改了图像,我必须从数据库和文件系统中删除旧图像,然后我必须添加新图像(在数据库和文件系统中)。
问题:如果我编辑一个已经在数据库中获得图像的文档(所以如果$oldImage = $this->getImageByDocumentId($docId) 实际上返回$oldImage),一切正常。但是如果Document 没有任何$oldImage,就会出现问题并且它不会在数据库中插入新图像(但它会将其保存在文件系统中!)
这是我MySQLDocumentService的一部分:
public function editDocument($document) {
try {
$conn = $this->getAdapter();
$conn->beginTransaction();
$sql = "UPDATE Documents d
SET d.name=:name, d.description=:description, d.content_id=:contentId, d.category_id=:categoryId, d.sharer_id=:sharerId, d.rating_id=:ratingId, d.price=:price
WHERE d.document_id=:id";
$prepStatement = $conn->prepare($sql);
$prepStatement->execute(array(':id' => $document->getId(),
':name' => $document->getName(),
':description' => $document->getDescription(),
':contentId' => rand(1,2000),
':categoryId' => $document->getCategory()->getId(),
':sharerId' => 1,
':ratingId' => 1,
':price' => $document->getPrice()));
// If image has been changed, take the old image name
if (!is_null($document->getImage())) {
$image = $document->getImage();
$docId = $document->getId();
$oldImage = $this->getImageByDocumentId($docId); // Here's the problem: if it doesn't find the oldImage, it doesn't insert the new one
if (!is_null($oldImage)) {
// If previous method succeeded, delete oldImage from DB and filesystem
$oldImageName = $oldImage->getName();
$this->deleteImageByName($oldImageName);
}
// Save new image (chosen on form) on db and filesystem
if (file_exists("uploads/img/" . $image->getName())) {
echo $image->getName() . " already exists. ";
return false;
} else {
move_uploaded_file($image->getTempName(), "uploads/img/" . $image->getName());
}
// Saves image path on DB
// If I edit a Document which has already got an image on the DB, everything works. But if the Document hasn't got any oldImage, something goes wrong and it doesn't insert the new Image on DB (but It saves it on filesystem!)
$sqlImage = 'INSERT INTO Images (name, alt_name, position, description, type, size, document_id)
VALUES ("name", "altName", 2, "description", "type", "size", 2)';
$prepStatementImg = $conn->prepare($sqlImage);
$prepStatementImg->execute();
}
$conn->commit();
return true;
} catch (Exception $e) {
$conn->rollBack();
echo "Failed: " . $e->getMessage();
}
}
public function getImageByDocumentId($docId) {
try {
$conn = $this->getAdapter();
$conn->beginTransaction();
$sql = 'SELECT i.image_id, i.document_id, i.name, i.alt_name, i.position, i.description, i.type, i.size
FROM Images i
WHERE i.document_id=:id';
$prepStatement = $conn->prepare($sql);
$prepStatement->execute(array(':id' => $docId));
$result = $prepStatement->fetch();
if ($result) {
$image = new Image();
$image->setName($result['name']);
$image->setId($result['image_id']);
$image->setAltName($result['alt_name']);
$image->setDescription($result['description']);
$image->setPosition($result['position']);
$image->setType($result['type']);
$image->setSize($result['size']);
// Manca la costruzione del relativo documento, ma non penso serva
$conn->commit();
return $image;
} else {
return null;
}
} catch (Exception $e) {
$conn->rollBack();
echo "Failed: " . $e->getMessage();
}
}
public function deleteImageByName($imgName) {
try {
$imgName = str_replace( array( '..', '/', '\\', ':' ), '', $imgName );
unlink( "uploads/img/" . $imgName );
} catch (Exception $fsEx) {
echo "Failed: " . $fsEx->getMessage();
}
try {
$conn = $this->getAdapter();
$conn->beginTransaction();
$sql = 'DELETE FROM Images
WHERE name=:name';
$prepStatement = $conn->prepare($sql);
$prepStatement->execute(array(':name' => $imgName));
$conn->commit();
return true;
} catch (Exception $e) {
$conn->rollBack();
echo "Failed: " . $e->getMessage();
}
}
如果我评论 $oldImage = $this->getImageByDocumentId($docId),它会在 DB 上提交新图像的 INSERT 并且一切正常。
我认为这可能是嵌套事务的问题,但这很奇怪,因为当在 db 上正确找到 $oldImage 时一切正常。 (我还创建了一个扩展 PDO 类的类,如 this guide 所写)。
我能做什么?
编辑:在下面的一个好回答中(由 Soyale 提供),对嵌套方法和多重转换产生了怀疑。因此,我粘贴了我的MyPDO 类,它应该避免多次转换(至少我希望如此)。这是bitluni对PDO::beginTransaction manual page的评论。
class MyPDO extends PDO {
protected $transactionCounter = 0;
function beginTransaction()
{
if(!$this->transactionCounter++)
return parent::beginTransaction();
return $this->transactionCounter >= 0;
}
function commit()
{
if(!--$this->transactionCounter)
return parent::commit();
return $this->transactionCounter >= 0;
}
function rollback()
{
if($this->transactionCounter >= 0)
{
$this->transactionCounter = 0;
return parent::rollback();
}
$this->transactionCounter = 0;
return false;
}
}
正如索亚尔所说,
parent::openTransaction[我认为这是beginTransaction()的拼写错误] 也不是一个好主意。或者,如果你有一些标志表明其中一项交易已经打开,它可以通过考试。
我认为transactionCounter 可能是您所说的标志。在我看来,这会让我正确地提交和回滚。我错了吗?
【问题讨论】:
-
你的问题我不清楚,但听起来你必须更新图像而不是插入它
-
我必须删除旧图像并插入新图像 :)
-
为什么要插入:INSERT INTO Images (name, alt_name, position, description, type, size, document_id) VALUES ("name", "altName", 2, "description", "type" , "size", 2) 完全没有变量。
-
@CodeBird 是因为我想缩短代码。实际版本更长。但这不是问题,因为当找到
$oldImage时插入工作完美无缺。 -
@KurtBourbaki 我已经编辑了我的答案并分析了为什么您的解决方案不太好
标签: php pdo transactions