1  <?php
  2  /* Copyright (c) 2012, Geert Bergman (geert@scrivo.nl)
  3   * All rights reserved.
  4   *
  5   * Redistribution and use in source and binary forms, with or without
  6   * modification, are permitted provided that the following conditions are met:
  7   *
  8   * 1. Redistributions of source code must retain the above copyright notice,
  9   *    this list of conditions and the following disclaimer.
 10   * 2. Redistributions in binary form must reproduce the above copyright notice,
 11   *    this list of conditions and the following disclaimer in the documentation
 12   *    and/or other materials provided with the distribution.
 13   * 3. Neither the name of "Scrivo" nor the names of its contributors may be
 14   *    used to endorse or promote products derived from this software without
 15   *    specific prior written permission.
 16   *
 17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 18   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 19   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 20   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 21   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 22   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 23   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 24   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 25   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 26   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 27   * POSSIBILITY OF SUCH DAMAGE.
 28   *
 29   * $Id: SequenceNo.php 866 2013-08-25 16:22:35Z geert $
 30   */
 31  
 32  /**
 33   * Implementation of the \Scrivo\SequenceNo class.
 34   */
 35  
 36  namespace Scrivo;
 37  
 38  /**
 39   * Class to manage the sequence (or order) numbers of various Scrivo entities.
 40   * Most Scrivo entitites like pages and lists items can be ordered manually.
 41   * This is done by setting a sequence number field in the database for say
 42   * all pages with a common parent.
 43   *
 44   * Note that this ordering has nothing to do with database sequence generation
 45   * used for generating ids.
 46   */
 47  class SequenceNo {
 48  
 49      /**
 50       * Constant to indicate that we want the entity to move one position up.
 51       */
 52      const MOVE_UP = -1;
 53  
 54      /**
 55       * Constant to indicate that we want the entity to move one position down.
 56       */
 57      const MOVE_DOWN = -2;
 58  
 59      /**
 60       * Constant to indicate that we want to move the entity to the beginning.
 61       */
 62      const MOVE_FIRST = -3;
 63  
 64      /**
 65       * Constant to indicate that we want to move the entity to the end.
 66       */
 67      const MOVE_LAST = -4;
 68  
 69      /**
 70       * Regenerate all sequence numbers for a set of entities determined by
 71       * the parent of the entity with the given $id. The new sequence numbers
 72       * will start with and will have an offset of 2. Sequence numbers with
 73       * a value of zero or less will not be updated.
 74       *
 75       * @param \Scrivo\Context $context A Scrivo context.
 76       * @param string $tableName The table name for the entity to resequence.
 77       * @param string $parentField The parent id field in the table for the
 78       *     entity to resequence.
 79       * @param int $id The id of the entity to resequence.
 80       */
 81      private static function resequence(
 82              $context$tableName$parentField$id) {
 83  
 84          $pJoin "";
 85          if (!is_array($parentField)) {
 86              $parentField = array($parentField);
 87          }
 88          foreach ($parentField as $pf) {
 89              $pJoin .= str_replace("[P]"$pf" AND D1.[P] = D2.[P]");
 90          }
 91  
 92          $sth $context->connection->prepare(
 93              str_replace("[T]"$tableName,
 94              "UPDATE [T] T, (
 95                  SELECT @n:=@n+2 NEW_SEQ, D1.instance_id, D1.[T]_id FROM
 96                      (SELECT @n:=0) x, [T] D1, [T] D2 WHERE
 97                      D1.instance_id = :instId AND D2.instance_id = :instId
 98                      {$pJoin} AND D2.[T]_id = :id AND
 99                      D1.sequence_no > 0 ORDER BY D1.sequence_no) R
100              SET T.sequence_no = R.NEW_SEQ
101              WHERE (T.instance_id = R.instance_id AND T.[T]_id = R.[T]_id)"));
102  
103          $context->connection->bindInstance($sth);
104          $sth->bindValue(":id"$id, \PDO::PARAM_INT);
105  
106          $sth->execute();
107      }
108  
109      /**
110       * Move an entity to a given position. Note that this is a postion number
111       * starting at 1, not a zero based index. Instead of an actual position
112       * number also one of \Scrivo\SequenceNo::MOVE_* constants can be used.
113       *
114       * @param \Scrivo\Context $context A Scrivo context.
115       * @param string $tableName The table name for the entity to resequence.
116       * @param string $parentField The parent id field in the table for the
117       *    entity to resequence.
118       * @param int $id The id of the entity to resequence.
119       * @param int $pos The new position of the entity to resequence, note that
120       *    this is a postion number starting at 1, not a zero based index.
121       */
122      public static function position(
123              \Scrivo\Context$context$tableName$parentField$id$pos) {
124          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
125              nullnullnull,
126              array(\Scrivo\ArgumentCheck::TYPE_INTEGER),
127              array(\Scrivo\ArgumentCheck::TYPE_INTEGER)
128          ));
129          try {
130  
131              self::resequence($context$tableName$parentField$id);
132  
133              $sth $context->connection->prepare(
134                  str_replace("[T]"$tableName,
135                  "SELECT sequence_no FROM [T] WHERE
136                      instance_id = :instId AND [T]_id = :id"));
137  
138              $context->connection->bindInstance($sth);
139              $sth->bindValue(":id"$id, \PDO::PARAM_INT);
140  
141              $sth->execute();
142  
143              $seqNo $sth->fetchColumn(0);
144  
145              switch ($pos) {
146              case self::MOVE_UP:
147                  if ($seqNo 2) {
148                      $seqNo -= 3;
149                  }
150                  break;
151              case self::MOVE_DOWN:
152                  $seqNo += 3;
153                  break;
154              case self::MOVE_FIRST:
155                  $seqNo 1;
156                  break;
157              case self::MOVE_LAST:
158                  $seqNo 10000000;
159                  break;
160              default:
161                  if ($pos $seqNo) {
162                      $seqNo = ($pos-1) * 1;
163                  } else if ($pos $seqNo) {
164                      $seqNo $pos 1;
165                  }
166                  break;
167              }
168  
169              $sth $context->connection->prepare(
170                  str_replace("[T]"$tableName,
171                  "UPDATE [T] SET sequence_no = :seqNo WHERE
172                      instance_id = :instId AND [T]_id = :id"));
173  
174              $context->connection->bindInstance($sth);
175              $sth->bindValue(":seqNo"$seqNo, \PDO::PARAM_INT);
176              $sth->bindValue(":id"$id, \PDO::PARAM_INT);
177  
178              $sth->execute();
179  
180          } catch(\PDOException $e) {
181              throw new \Scrivo\ResourceException($e);
182          }
183  
184      }
185  
186  }
187  
188  ?>

Documentation generated by phpDocumentor 2.0.0a12 and ScrivoDocumentor on August 29, 2013