更新多对多联接表的最佳实践?

时间:2015-05-25 09:57:43

标签: php mysql many-to-many

这是一个关于如何在多对多连接表中最佳地分配或更新多个条目的问题。简而言之,能够修改文章或博客的类别或标签。

在我正在构建的这个应用程序中,我有一个Category表(类别)和Article表(称为musings)。

因为每篇文章都可以属于许多类别(并且每个类别可以分配给许多文章),所以我有一个这种关系的联接表(category_musings)。我已经设置了约束 - 因此删除文章会删除连接表中的所有关联条目。但是,如果当前已将类别分配给文章,则禁止删除该类别。

我想要达到的目的:

我希望最终用户能够更新/修改/删除给定文章的多个类别(沉思)。

我的工作如此:

我找到了一种方法,可以使用以下函数在多样式下拉列表中显示当前类别值

首先,我得到所有类别的列表

//Return list of categories
public function categories($column)
{
$sql = "SELECT * FROM category";
$query = $this->connection->prepare($sql);
$query->execute();

return $query->fetchAll(\PDO::FETCH_COLUMN, $column);;
}

然后分配给特定冥想的类别

//Return categories selection for each musing
public function getMusingCategoryList($id)
{
$sql = "SELECT category AS categories
              FROM musings 
              JOIN category_musings ON musings.musing_id=category_musings.musing_id
              JOIN category ON category_musings.category_id=category.category_id
              WHERE category_musings.musing_id=:musing_id";
$query = $this->connection->prepare($sql);

$values = [
':musing_id' => $id,
];
$query->execute($values);

return $query->fetchAll(\PDO::FETCH_COLUMN,0);
}

两个返回数组然后我传递给另一个函数来创建我的下拉列表。这是前端的PHP代码

$data = new \norm_musing\MusingData();
$Musing = $data->getMusing($_GET['id']);

$name = 'multi_dropdown';
$options = $data->categories("1");
$selected = $data->getMusingCategoryList($_GET['id']);

$result = $data->multi_dropdown( $name, $options, $selected );

这是下拉创建功能,我稍后会将其与所有其他相关的现场数据一起传递给表单

//Create dropdown for categories
public function multi_dropdown( $name, array $options, array $selected=null, $size=4 )
{
    /*** begin the select ***/
    $dropdown = '<select class="form-control" name="'.$name.'[]" id="'.$name.'" size="'.$size.'" multiple>'."\n";

    /*** loop over the options ***/
    foreach( $options as $key=>$option )
    {
            /*** assign a selected value ***/
            $select = in_array( $option, $selected ) ? ' selected' : null;

            /*** add each option to the dropdown ***/
            $dropdown .= '<option value="'.$key.'"'.$select.'>'.$option.'</option>'."\n";
    }

    /*** close the select ***/
    $dropdown .= '</select>'."\n";

    /*** and return the completed dropdown ***/
    return $dropdown;}

这将生成一个String,其中包含必要的Form HTML代码,用于创建下拉列表,并选择当前值(详细)。并且所有用户必须能够按住CTRL并同时用鼠标单击,如果他们想要进行更改。

但是现在我必须找到一种方法来更新Join表,如果用户更改了选择(或忽略,如果他们没有)。

下拉框以数组形式返回

$selection = $_POST['multi_dropdown']. 

e.g。共有6个类别 - 在列表中选择1和4 - 数组值为[0,3]

出于冗长的目的,所以我可以理解发生了什么,我创建这个PHP代码来处理我的表单响应

if (isset($_POST['id']) && !empty($_POST['id'])) 
{
$aCategories = $_POST['multi_dropdown'];
if(!isset($aCategories)) 
{
echo("<p>You didn't select any categories!</p>\n");
}
else
{
$nCategories = count($aCategories);
$categoryID = $data->categories("0");

echo("<p>You selected $nCategories categories: ");
for($i=0; $i < $nCategories; $i++)
{
  echo($aCategories[$i] . " ");
  echo($options[$aCategories[$i]] . " ");
  echo("(id: " . $categoryID[$aCategories[$i]] . ") | ");
}
echo("</p>");
$nCount = $_POST['count'];
$newSelected = $data->getMusingCategoryList($_POST['id']);
echo("<p>Originally you had $nCount categories: ");
for($i=0; $i < $nCount; $i++)
{
  echo($newSelected[$i] . " ");
}
echo("</p>");

exit;}}

生成这样的输出(id是类别表中的主键)

    You selected 2 categories: 0 Aging (id: 3) | 2 Health (id: 4) |

    Originally you had 1 categories: Happiness 

我的问题:

我应该简单地删除连接表中与Musing ID匹配的所有条目 - 然后插入新选择的INSERT吗?如果没有进行任何更改,那将是浪费的,所以首先我可能需要进行某种数组比较。

这里最好的做法是什么?

是否有一种更简单的方法允许用户为文章添加和更新多个类别或标签(例如博客条目或在这种情况下是沉思)?

2 个答案:

答案 0 :(得分:0)

我认为解决这个问题的最佳方法是利用MySQL的“INSERT IGNORE INTO”或者“REPLACE INTO”(但我认为这会引发2个问题)。

请参阅http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html以供参考。

答案 1 :(得分:0)

这是我的解决方案 - 我还没有看到REPLACE INTO或INSERT IGNORE INTO如何实现同样的目标

//Delete all categories for a given musing ID
public function deleteMusingCats( $id ) {

   $sql="DELETE FROM category_musings WHERE musing_id = :musing_id";
   $query = $this->connection->prepare($sql);
   $values = ['musing_id' => $id];
   $query->execute($values);

   }


//Insert new category for musing
public function updateMusingCats( $a, array $b, $n ) {  

$stmt = $this->connection->prepare("
                 INSERT INTO category_musings (category_id, musing_id) 
                 VALUES (:catID, :musID);
                                  ");  

for ($i=0; $i < $n; $i++) 
{
  $stmt->bindValue(':catID', $b[$i], \PDO::PARAM_INT);
  $stmt->bindValue(':musID', $a, \PDO::PARAM_INT);
  $stmt->execute();
}

return $i;
}   

我只调用发送到表单的数组和从表单接收回的数组之间的更改。

if (isset($_POST['id']) && !empty($_POST['id'])) 
{
    // Simple number array of the list items selected
    $aCategories = $_POST['multi_dropdown']; 

    if(!isset($aCategories))   {
        // No categories selected. Abort!
        header("Location: /norms_musings");
    } else {
        $nCategories = count($aCategories); // Number of categories selected
        $categoryID = $data->categories("0");  // Create complete category ID array

        for($i=0; $i < $nCategories; $i++) 
        /* Create array of selected categories the number value from the
        dropdown list corresponding to the place in the full category list array */
            $a[]=$options[$aCategories[$i]]; 
            $b[]=$categoryID[$aCategories[$i]]; // Create array of selected category IDs
        }

        $nCount = $_POST['count'];
        $newSelected = $data->getMusingCategoryList($_POST['id']);

        $equal = ($a==$newSelected);

        if ($equal) {
            // No Changes Made!
            header("Location: /norms_musings");
            exit;
        } else {
            $data->deleteMusingCats($_POST['id']);
            $arrayDiff = (array_diff($a,$newSelected));
        }

        if ($data->updateMusingCats($_POST['id'],$b,$nCategories)) {
            header("Location: /norms_musings");
            exit;
        } else {
            echo "An error occurred";
            exit;
        }
    }
}