function test_many_to_many_set_when_already_in_a_transaction() { $this->assertTrue(No2_SQLQuery::_beginTransaction()); $admin = User::all()->root()->select(); $admin->set_roles([Role::ADMIN_ID]); $roles = $admin->roles()->select(); $this->assertTrue(No2_SQLQuery::_commitTransaction()); $this->assertCount(1, $roles, 'root should have exactly one role'); $this->assertEquals(Role::ADMIN_ID, $roles[0]->id, 'the only root role should be ADMIN'); }
/** * 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; }
// load Composer stuff require_once PROJECTDIR . '/vendor/autoload.php'; // initialize no2 framework. require_once PROJECTDIR . '/no2/no2.inc.php'; // get the config stuff require_once APPDIR . '/config.class.php'; AppConfig::parse(PROJECTDIR . '/config/config.yml', array('{{APPDIR}}' => APPDIR, '{{PROJECTDIR}}' => PROJECTDIR, '{{WEBDIR}}' => WEBDIR)); // load the application's models. require_once APPDIR . '/models/user.class.php'; // load the application's helpers. require_once APPDIR . '/help.inc.php'; // set the timezone date_default_timezone_set(AppConfig::get('l10n.default_timezone')); // set the locale setlocale(LC_ALL, AppConfig::get('l10n.default_locale')); // start the logger if (!No2_Logger::setup(AppConfig::get('logger'))) { error_log('unable to setup Logger'); } // connect to the database. No2_SQLQuery::setup(AppConfig::get('database')); // try our best to hide the fact that we still use PHP in the 21th century. if (function_exists('header_remove')) { header_remove('X-Powered-By'); // PHP 5.3+ } else { @ini_set('expose_php', 'off'); } // start the session session_set_cookie_params(0, dirname($_SERVER['SCRIPT_NAME'])); session_start() or die('session_start()');
/** * Perform a DELETE instruction to remove the associated row with this * object in the database. * * This function doesn't check for association, it doesn't cleanup * dependencies, and doesn't throw an error when it fails. For all theses * reasons direct use of this method is highly discouraged, destroy() * should be called instead. As a result it is protected and prefixed by an * underline. A subclass that *really* want to make this method available * (meaning bypassing destroy()) would have to define public delete() * method forwarding the call to _delete(): * @code * public function delete() * { * return $this->_delete(); * } * @endcode * * @return * false on error, true otherwise */ protected function _delete() { $query = new No2_SQLQuery(get_class($this)); $query->query_on($this->__db_profile); return $query->id($this->id)->delete(); }
/** * set the database profile to use for this query. * * @throw * InvalidArgumentException if a bad database profile is given. */ public function query_on($profile) { No2_SQLQuery::_database_or_throw($profile); // checking validity $profiled = $this->specialize(); $profiled->profile = $profile; return $profiled; }