/**
  * Links this model to the others: handles all the stuff about the join table.
  *
  * @param
  *  array $relation: The relation to use
  *
  *  Define the relation with an array containing 3 values:
  *    - The target key: the column linked to this model in the join table
  *    - The join table name
  *    - The linked key: the column linked to the other model in the join table
  *
  * @param
  *  array $others: An array of Models or of ids
  *
  * @throws
  *  InvalidArgumentException when the relation is not well defined
  *  LogicException when $this is a new record
  *
  * @return
  *  boolean: true on success, false otherwise
  */
 protected function many_to_many_set($relation, $others = null)
 {
     if ($this->is_new_record()) {
         throw new LogicException('many_to_many_set called on a new record');
     }
     if (!is_array($relation) || count($relation) < 3) {
         throw new InvalidArgumentException('The relation is not defined');
     }
     list($target_key, $join_table, $linked_key) = $relation;
     $delete = 'DELETE FROM {join_table} WHERE {target_key} = :id';
     $insert = 'INSERT INTO {join_table} ({target_key}, {linked_key}) VALUES';
     $insert_params = $delete_params = ['{join_table}' => $join_table, '{target_key}' => $target_key, '{linked_key}' => $linked_key, ':id' => $this->id];
     // build the VALUES (...) for the INSERT statment
     $values = [];
     if (is_array($others) && !empty($others)) {
         for ($i = 0, $n = count($others); $i < $n; $i++) {
             $other = $others[$i];
             $label = ":val_{$i}";
             $values[] = "(:id, {$label})";
             $insert_params[$label] = is_object($other) && isset($other->id) ? $other->id : $other;
         }
     }
     $values = join(', ', $values);
     $profile = $this->__db_profile;
     $options = ['profile' => $profile];
     if (empty($values)) {
         $success = No2_SQLQuery::execute($delete, $delete_params, $options) !== false;
     } else {
         // start a transaction if we're not already in one.
         $already_in_transaction = No2_SQLQuery::_inTransaction();
         if (!$already_in_transaction) {
             No2_SQLQuery::_beginTransaction($profile);
         }
         // do the work
         $success = No2_SQLQuery::execute($delete, $delete_params, $options) !== false && No2_SQLQuery::execute("{$insert} {$values}", $insert_params, $options) !== false;
         // terminate the transaction if we started it.
         if (!$already_in_transaction) {
             if ($success) {
                 No2_SQLQuery::_commitTransaction($profile);
             } else {
                 No2_SQLQuery::_rollBackTransaction($profile);
             }
         }
     }
     return $success;
 }