Overview

Namespaces

  • DataTables
    • Database
    • Editor
    • Vendor

Classes

  • DataTables\Database
  • DataTables\Database\Query
  • DataTables\Database\Result
  • DataTables\Editor
  • DataTables\Editor\Field
  • DataTables\Editor\Format
  • DataTables\Editor\Join
  • DataTables\Editor\MJoin
  • DataTables\Editor\Options
  • DataTables\Editor\Upload
  • DataTables\Editor\Validate
  • DataTables\Editor\ValidateOptions
  • DataTables\Ext
  • DataTables\Vendor\Htmlaw
  • DataTables\Vendor\htmLawed
  • Overview
  • Namespace
  • Class
  1: <?php
  2: /**
  3:  * DataTables PHP libraries.
  4:  *
  5:  * PHP libraries for DataTables and DataTables Editor, utilising PHP 5.3+.
  6:  *
  7:  *  @author    SpryMedia
  8:  *  @copyright 2012 SpryMedia ( http://sprymedia.co.uk )
  9:  *  @license   http://editor.datatables.net/license DataTables Editor
 10:  *  @link      http://editor.datatables.net
 11:  */
 12: 
 13: namespace DataTables\Editor;
 14: if (!defined('DATATABLES')) exit();
 15: 
 16: use
 17:     DataTables,
 18:     DataTables\Editor,
 19:     DataTables\Editor\Field;
 20: 
 21: 
 22: /**
 23:  * Join table class for DataTables Editor.
 24:  *
 25:  * The Join class can be used with {@link Editor::join} to allow Editor to
 26:  * obtain joined information from the database.
 27:  *
 28:  * For an overview of how Join tables work, please refer to the 
 29:  * {@link https://editor.datatables.net/manual/php/ Editor manual} as it is
 30:  * useful to understand how this class represents the links between tables, 
 31:  * before fully getting to grips with it's API.
 32:  *
 33:  *  @example
 34:  *    Join the parent table (the one specified in the {@link Editor::table}
 35:  *    method) with the table *access*, with a link table *user__access*, which
 36:  *    allows multiple properties to be found for each row in the parent table.
 37:  *    <code>
 38:  *      Join::inst( 'access', 'array' )
 39:  *          ->link( 'users.id', 'user_access.user_id' )
 40:  *          ->link( 'access.id', 'user_access.access_id' )
 41:  *          ->field(
 42:  *              Field::inst( 'id' )->validator( 'Validate::required' ),
 43:  *              Field::inst( 'name' )
 44:  *          )
 45:  *    </code>
 46:  *
 47:  *  @example
 48:  *    Single row join - here we join the parent table with a single row in
 49:  *    the child table, without an intermediate link table. In this case the
 50:  *    child table is called *extra* and the two fields give the columns that
 51:  *    Editor will read from that table.
 52:  *    <code>
 53:  *        Join::inst( 'extra', 'object' )
 54:  *            ->link( 'user.id', 'extra.user_id' )
 55:  *            ->field(
 56:  *                Field::inst( 'comments' ),
 57:  *                Field::inst( 'review' )
 58:  *            )
 59:  *    </code>
 60:  */
 61: class Join extends DataTables\Ext {
 62:     /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 63:      * Constructor
 64:      */
 65: 
 66:     /**
 67:      * Join instance constructor.
 68:      *  @param string $table Table name to get the joined data from.
 69:      *  @param string $type Work with a single result ('object') or an array of 
 70:      *    results ('array') for the join.
 71:      */
 72:     function __construct( $table=null, $type='object' )
 73:     {
 74:         $this->table( $table );
 75:         $this->type( $type );
 76:     }
 77: 
 78: 
 79:     /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 80:      * Private properties
 81:      */
 82: 
 83:     /** @var DataTables\Editor\Field[] */
 84:     private $_fields = array();
 85: 
 86:     /** @var array */
 87:     private $_join = array(
 88:         "parent" => null,
 89:         "child" => null,
 90:         "table" => null
 91:     );
 92: 
 93:     /** @var string */
 94:     private $_table = null;
 95: 
 96:     /** @var string */
 97:     private $_type = null;
 98: 
 99:     /** @var string */
100:     private $_name = null;
101: 
102:     /** @var boolean */
103:     private $_get = true;
104: 
105:     /** @var boolean */
106:     private $_set = true;
107: 
108:     /** @var string */
109:     private $_aliasParentTable = null;
110: 
111:     /** @var array */
112:     private $_where = array();
113: 
114:     /** @var boolean */
115:     private $_whereSet = false;
116: 
117:     /** @var array */
118:     private $_links = array();
119: 
120:     /** @var string */
121:     private $_customOrder = null;
122: 
123: 
124:     /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
125:      * Public methods
126:      */
127:     
128:     /**
129:      * Get / set parent table alias.
130:      * 
131:      * When working with a self referencing table (i.e. a column in the table contains
132:      * a primary key value from its own table) it can be useful to set an alias on the
133:      * parent table's name, allowing a self referencing Join. For example:
134:      *   <code>
135:      *   SELECT p2.publisher 
136:      *   FROM   publisher as p2
137:      *   JOIN   publisher on (publisher.idPublisher = p2.idPublisher)
138:      *   <code>
139:      * Where, in this case, `publisher` is the table that is used by the Editor instance,
140:      * and you want to use `Join` to link back to the table (resolving a name for example).
141:      * This method allows that alias to be set. Fields can then use standard SQL notation
142:      * to select a field, for example `p2.publisher` or `publisher.publisher`.
143:      *  @param string $_ Table alias to use
144:      *  @return string|self Table alias set (which is null by default), or self if used as
145:      *    a setter.
146:      */
147:     public function aliasParentTable ( $_=null )
148:     {
149:         return $this->_getSet( $this->_aliasParentTable, $_ );
150:     }
151: 
152: 
153:     /**
154:      * Get / set field instances.
155:      * 
156:      * The list of fields designates which columns in the table that will be read
157:      * from the joined table.
158:      *  @param Field $_... Instances of the {@link Field} class, given as a single 
159:      *    instance of {@link Field}, an array of {@link Field} instances, or multiple
160:      *    {@link Field} instance parameters for the function.
161:      *  @return Field[]|self Array of fields, or self if used as a setter.
162:      *  @see {@link Field} for field documentation.
163:      */
164:     public function field ( $_=null )
165:     {
166:         if ( $_ !== null && !is_array($_) ) {
167:             $_ = func_get_args();
168:         }
169:         return $this->_getSet( $this->_fields, $_, true );
170:     }
171: 
172: 
173:     /**
174:      * Get / set field instances.
175:      * 
176:      * An alias of {@link field}, for convenience.
177:      *  @param Field $_... Instances of the {@link Field} class, given as a single 
178:      *    instance of {@link Field}, an array of {@link Field} instances, or multiple
179:      *    {@link Field} instance parameters for the function.
180:      *  @return Field[]|self Array of fields, or self if used as a setter.
181:      *  @see {@link Field} for field documentation.
182:      */
183:     public function fields ( $_=null )
184:     {
185:         if ( $_ !== null && !is_array($_) ) {
186:             $_ = func_get_args();
187:         }
188:         return $this->_getSet( $this->_fields, $_, true );
189:     }
190: 
191: 
192:     /**
193:      * Get / set get attribute.
194:      * 
195:      * When set to false no read operations will occur on the join tables.
196:      *  @param boolean $_ Value
197:      *  @return boolean|self Name
198:      */
199:     public function get ( $_=null )
200:     {
201:         return $this->_getSet( $this->_get, $_ );
202:     }
203: 
204: 
205:     /**
206:      * Get / set join properties.
207:      *
208:      * Define how the SQL will be performed, on what columns. There are
209:      * basically two types of join that are supported by Editor here, a direct
210:      * foreign key reference in the join table to the parent table's primary
211:      * key, or a link table that contains just primary keys for the parent and
212:      * child tables (this approach is usually used with a {@link type} of
213:      * 'array' since you can often have multiple links between the two tables,
214:      * while a direct foreign key reference will typically use a type of
215:      * 'object' (i.e. a single entry).
216:      *
217:      *  @param string|string[] $parent Parent table's primary key names. If used
218:      *    with a link table (i.e. third parameter to this method is given, then
219:      *    an array should be used, with the first element being the pkey's name
220:      *    in the parent table, and the second element being the key's name in
221:      *    the link table.
222:      *  @param string|string[] $child Child table's primary key names. If used
223:      *    with a link table (i.e. third parameter to this method is given, then
224:      *    an array should be used, with the first element being the pkey's name
225:      *    in the child table, and the second element being the key's name in the
226:      *    link table.
227:      *  @param string $table Join table name, if using a link table
228:      *  @returns Join This for chaining
229:      *  @deprecated 1.5 Please use the {@link link} method rather than this
230:      *      method now.
231:      */
232:     public function join ( $parent=null, $child=null, $table=null )
233:     {
234:         if ( $parent === null && $child === null ) {
235:             return $this->_join;
236:         }
237: 
238:         $this->_join['parent'] = $parent;
239:         $this->_join['child'] = $child;
240:         $this->_join['table'] = $table;
241:         return $this;
242:     }
243: 
244: 
245:     /**
246:      * Create a join link between two tables. The order of the fields does not
247:      * matter, but each field must contain the table name as well as the field
248:      * name.
249:      * 
250:      * This method can be called a maximum of two times for an Mjoin instance:
251:      * 
252:      * * First time, creates a link between the Editor host table and a join
253:      *   table
254:      * * Second time creates the links required for a link table.
255:      * 
256:      * Please refer to the Editor Mjoin documentation for further details:
257:      * https://editor.datatables.net/manual/php
258:      *
259:      * @param  string $field1 Table and field name
260:      * @param  string $field2 Table and field name
261:      * @return Join Self for chaining
262:      * @throws \Exception Link limitations
263:      */
264:     public function link ( $field1, $field2 )
265:     {
266:         if ( strpos($field1, '.') === false || strpos($field2, '.') === false ) {
267:             throw new \Exception("Link fields must contain both the table name and the column name");
268:         }
269: 
270:         if ( count( $this->_links ) >= 4 ) {
271:             throw new \Exception("Link method cannot be called more than twice for a single instance");
272:         }
273: 
274:         $this->_links[] = $field1;
275:         $this->_links[] = $field2;
276: 
277:         return $this;
278:     }
279: 
280: 
281:     /**
282:      * Specify the property that the data will be sorted by.
283:      *
284:      * @param  string $order SQL column name to order the data by
285:      * @return Join Self for chaining
286:      */
287:     public function order ( $_=null )
288:     {
289:         // How this works is by setting the SQL order by clause, and since the
290:         // join that is done in PHP is always done sequentially, the order is
291:         // retained.
292:         return $this->_getSet( $this->_customOrder, $_ );
293:     }
294: 
295: 
296:     /**
297:      * Get / set name.
298:      * 
299:      * The `name` of the Join is the JSON property key that is used when 
300:      * 'getting' the data, and the HTTP property key (in a JSON style) when
301:      * 'setting' data. Typically the name of the db table will be used here,
302:      * but this method allows that to be overridden.
303:      *  @param string $_ Field name
304:      *  @return String|self Name
305:      */
306:     public function name ( $_=null )
307:     {
308:         return $this->_getSet( $this->_name, $_ );
309:     }
310: 
311: 
312:     /**
313:      * Get / set set attribute.
314:      * 
315:      * When set to false no write operations will occur on the join tables.
316:      * This can be useful when you want to display information which is joined,
317:      * but want to only perform write operations on the parent table.
318:      *  @param boolean $_ Value
319:      *  @return boolean|self Name
320:      */
321:     public function set ( $_=null )
322:     {
323:         return $this->_getSet( $this->_set, $_ );
324:     }
325: 
326: 
327:     /**
328:      * Get / set join table name.
329:      *
330:      * Please note that this will also set the {@link name} used by the Join
331:      * as well. This is for convenience as the JSON output / HTTP input will
332:      * typically use the same name as the database name. If you want to set a
333:      * custom name, the {@link name} method must be called ***after*** this one.
334:      *  @param string $_ Name of the table to read the join data from
335:      *  @return String|self Name of the join table, or self if used as a setter.
336:      */
337:     public function table ( $_=null )
338:     {
339:         if ( $_ !== null ) {
340:             $this->_name = $_;
341:         }
342:         return $this->_getSet( $this->_table, $_ );
343:     }
344: 
345: 
346:     /**
347:      * Get / set the join type.
348:      * 
349:      * The join type allows the data that is returned from the join to be given
350:      * as an array (i.e. working with multiple possibly results for each record from
351:      * the parent table), or as an object (i.e. working which one and only one result
352:      * for each record form the parent table).
353:      *  @param string $_ Join type ('object') or an array of 
354:      *    results ('array') for the join.
355:      *  @return String|self Join type, or self if used as a setter.
356:      */
357:     public function type ( $_=null )
358:     {
359:         return $this->_getSet( $this->_type, $_ );
360:     }
361: 
362: 
363:     /**
364:      * Where condition to add to the query used to get data from the database.
365:      * Note that this is applied to the child table.
366:      * 
367:      * Can be used in two different ways:
368:      * 
369:      * * Simple case: `where( field, value, operator )`
370:      * * Complex: `where( fn )`
371:      *
372:      *  @param string|callable $key   Single field name or a closure function
373:      *  @param string|string[] $value Single field value, or an array of values.
374:      *  @param string          $op    Condition operator: <, >, = etc
375:      *  @return string[]|self Where condition array, or self if used as a setter.
376:      */
377:     public function where ( $key=null, $value=null, $op='=' )
378:     {
379:         if ( $key === null ) {
380:             return $this->_where;
381:         }
382: 
383:         if ( is_callable($key) && is_object($key) ) {
384:             $this->_where[] = $key;
385:         }
386:         else {
387:             $this->_where[] = array(
388:                 "key"   => $key,
389:                 "value" => $value,
390:                 "op"    => $op
391:             );
392:         }
393: 
394:         return $this;
395:     }
396: 
397: 
398:     /**
399:      * Get / set if the WHERE conditions should be included in the create and
400:      * edit actions.
401:      * 
402:      * This means that the fields which have been used as part of the 'get'
403:      * WHERE condition (using the `where()` method) will be set as the values
404:      * given.
405:      *
406:      * This is default false (i.e. they are not included).
407:      *
408:      *  @param boolean $_ Include (`true`), or not (`false`)
409:      *  @return boolean Current value
410:      */
411:     public function whereSet ( $_=null )
412:     {
413:         return $this->_getSet( $this->_whereSet, $_ );
414:     }
415: 
416: 
417: 
418:     /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
419:      * Internal methods
420:      */
421: 
422:     /**
423:      * Get data
424:      *  @param Editor $editor Host Editor instance
425:      *  @param string[] $data Data from the parent table's get and were we need
426:      *    to add out output.
427:      *  @param array $options options array for fields
428:      *  @internal
429:      */
430:     public function data( $editor, &$data, &$options )
431:     {
432:         if ( ! $this->_get ) {
433:             return;
434:         }
435: 
436:         $this->_prep( $editor );
437:         $db       = $editor->db();
438:         $dteTable = $editor->table();
439:         $pkey     = $editor->pkey();
440:         $idPrefix = $editor->idPrefix();
441: 
442:         $dteTable = $dteTable[0];
443:         $dteTableLocal = $this->_aliasParentTable ? // Can be aliased to allow a self join
444:             $this->_aliasParentTable :
445:             $dteTable;
446:         $joinField = isset($this->_join['table']) ?
447:             $this->_join['parent'][0] :
448:             $this->_join['parent'];
449: 
450:         // This is something that will likely come in a future version, but it
451:         // is a relatively low use feature. Please get in touch if this is
452:         // something you require.
453:         if ( count( $pkey ) > 1 ) {
454:             throw new \Exception("MJoin is not currently supported with a compound primary key for the main table", 1);
455:         }
456: 
457:         if ( count($data) > 0 ) {
458:             $pkey = $pkey[0];
459:             $pkeyIsJoin = $pkey === $joinField || $pkey === $dteTable.'.'.$joinField;
460: 
461:             // Sanity check that table selector fields are read only, and have an name without
462:             // a dot (for DataTables mData to be able to read it)
463:             for ( $i=0 ; $i<count($this->_fields) ; $i++ ) {
464:                 $field = $this->_fields[$i];
465: 
466:                 if ( strpos( $field->dbField() , "." ) !== false ) {
467:                     if ( $field->set() !== Field::SET_NONE && $this->_set ) {
468:                         echo json_encode( array(
469:                             "sError" => "Table selected fields (i.e. '{table}.{column}') in `Join` ".
470:                                 "must be read only. Use `set(false)` for the field to disable writing."
471:                         ) );
472:                         exit(0);
473:                     }
474: 
475:                     if ( strpos( $field->name() , "." ) !== false ) {
476:                         echo json_encode( array(
477:                             "sError" => "Table selected fields (i.e. '{table}.{column}') in `Join` ".
478:                                 "must have a name alias which does not contain a period ('.'). Use ".
479:                                 "name('---') to set a name for the field"
480:                         ) );
481:                         exit(0);
482:                     }
483:                 }
484:             }
485: 
486:             // Set up the JOIN query
487:             $stmt = $db
488:                 ->query( 'select' )
489:                 ->distinct( true )
490:                 ->get( $dteTableLocal.'.'.$joinField.' as dteditor_pkey' )
491:                 ->get( $this->_fields('get') )
492:                 ->table( $dteTable .' as '. $dteTableLocal );
493: 
494:             if ( $this->order() ) {
495:                 $stmt->order( $this->order() );
496:             }
497: 
498:             $this->_apply_where( $stmt );
499: 
500:             if ( isset($this->_join['table']) ) {
501:                 // Working with a link table
502:                 $stmt
503:                     ->join(
504:                         $this->_join['table'],
505:                         $dteTableLocal.'.'.$this->_join['parent'][0] .' = '. $this->_join['table'].'.'.$this->_join['parent'][1]
506:                     )
507:                     ->join(
508:                         $this->_table,
509:                         $this->_table.'.'.$this->_join['child'][0] .' = '. $this->_join['table'].'.'.$this->_join['child'][1]
510:                     );
511:             }
512:             else {
513:                 // No link table in the middle
514:                 $stmt
515:                     ->join(
516:                         $this->_table,
517:                         $this->_table.'.'.$this->_join['child'] .' = '. $dteTableLocal.'.'.$this->_join['parent']
518:                     );
519:             }
520: 
521:             // Check that the joining field is available.  The joining key can
522:             // come from the Editor instance's primary key, or any other field,
523:             // including a nested value (from a left join). If the instance's 
524:             // pkey, then we've got that in the DT_RowId parameter, so we can
525:             // use that. Otherwise, the key must be in the field list.
526:             if ( $this->_propExists( $dteTable.'.'.$joinField, $data[0] ) ) {
527:                 $readField = $dteTable.'.'.$joinField;
528:             }
529:             else if ( $this->_propExists( $joinField, $data[0] ) ) {
530:                 $readField = $joinField;
531:             }
532:             else if ( ! $pkeyIsJoin ) {
533:                 echo json_encode( array(
534:                     "sError" => "Join was performed on the field '{$joinField}' which was not "
535:                         ."included in the Editor field list. The join field must be included "
536:                         ."as a regular field in the Editor instance."
537:                 ) );
538:                 exit(0);
539:             }
540: 
541:             // Get list of pkey values and apply as a WHERE IN condition
542:             // This is primarily useful in server-side processing mode and when filtering
543:             // the table as it means only a sub-set will be selected
544:             // This is only applied for "sensible" data sets. It will just complicate
545:             // matters for really large data sets:
546:             // https://stackoverflow.com/questions/21178390/in-clause-limitation-in-sql-server
547:             if ( count($data) < 1000 ) {
548:                 $whereIn = array();
549: 
550:                 for ( $i=0 ; $i<count($data) ; $i++ ) {
551:                     $whereIn[] = $pkeyIsJoin ? 
552:                         str_replace( $idPrefix, '', $data[$i]['DT_RowId'] ) :
553:                         $this->_readProp( $readField, $data[$i] );
554:                 }
555: 
556:                 $stmt->where_in( $dteTableLocal.'.'.$joinField, $whereIn );
557:             }
558: 
559:             // Go!
560:             $res = $stmt->exec();
561:             if ( ! $res ) {
562:                 return;
563:             }
564: 
565:             // Map to primary key for fast lookup
566:             $join = array();
567:             while ( $row=$res->fetch() ) {
568:                 $inner = array();
569: 
570:                 for ( $j=0 ; $j<count($this->_fields) ; $j++ ) {
571:                     $field = $this->_fields[$j];
572:                     if ( $field->apply('get') ) {
573:                         $inner[ $field->name() ] = $field->val('get', $row);
574:                     }
575:                 }
576: 
577:                 if ( $this->_type === 'object' ) {
578:                     $join[ $row['dteditor_pkey'] ] = $inner;
579:                 }
580:                 else {
581:                     if ( !isset( $join[ $row['dteditor_pkey'] ] ) ) {
582:                         $join[ $row['dteditor_pkey'] ] = array();
583:                     }
584:                     $join[ $row['dteditor_pkey'] ][] = $inner;
585:                 }
586:             }
587: 
588:             // Loop over the data and do a join based on the data available
589:             for ( $i=0 ; $i<count($data) ; $i++ ) {
590:                 $rowPKey = $pkeyIsJoin ? 
591:                     str_replace( $idPrefix, '', $data[$i]['DT_RowId'] ) :
592:                     $this->_readProp( $readField, $data[$i] );
593: 
594:                 if ( isset( $join[$rowPKey] ) ) {
595:                     $data[$i][ $this->_name ] = $join[$rowPKey];
596:                 }
597:                 else {
598:                     $data[$i][ $this->_name ] = ($this->_type === 'object') ?
599:                         (object)array() : array();
600:                 }
601:             }
602:         }
603: 
604:         // Field options
605:         foreach ($this->_fields as $field) {
606:             $opts = $field->optionsExec( $db );
607: 
608:             if ( $opts !== false ) {
609:                 $name = $this->name().
610:                     ($this->_type === 'object' ? '.' : '[].').
611:                     $field->name();
612:                 $options[ $name ] = $opts;
613:             }
614:         }
615:     }
616: 
617: 
618:     /**
619:      * Create a row.
620:      *  @param Editor $editor Host Editor instance
621:      *  @param int $parentId Parent row's primary key value
622:      *  @param string[] $data Data to be set for the join
623:      *  @internal
624:      */
625:     public function create ( $editor, $parentId, $data )
626:     {
627:         // If not settable, or the many count for the join was not submitted
628:         // there we do nothing
629:         if (
630:             ! $this->_set ||
631:             ! isset($data[$this->_name]) || 
632:             ! isset($data[$this->_name.'-many-count'])
633:         ) {
634:             return;
635:         }
636: 
637:         $this->_prep( $editor );
638:         $db = $editor->db();
639:         
640:         if ( $this->_type === 'object' ) {
641:             $this->_insert( $db, $parentId, $data[$this->_name] );
642:         }
643:         else {
644:             for ( $i=0 ; $i<count($data[$this->_name]) ; $i++ ) {
645:                 $this->_insert( $db, $parentId, $data[$this->_name][$i] );
646:             }
647:         }
648:     }
649: 
650: 
651:     /**
652:      * Update a row.
653:      *  @param Editor $editor Host Editor instance
654:      *  @param int $parentId Parent row's primary key value
655:      *  @param string[] $data Data to be set for the join
656:      *  @internal
657:      */
658:     public function update ( $editor, $parentId, $data )
659:     {
660:         // If not settable, or the many count for the join was not submitted
661:         // there we do nothing
662:         if ( ! $this->_set || ! isset($data[$this->_name.'-many-count']) ) {
663:             return;
664:         }
665: 
666:         $this->_prep( $editor );
667:         $db = $editor->db();
668:         
669:         if ( $this->_type === 'object' ) {
670:             // update or insert
671:             $this->_update_row( $db, $parentId, $data[$this->_name] );
672:         }
673:         else {
674:             // WARNING - this will remove rows and then readd them. Any
675:             // data not in the field list WILL BE LOST
676:             // todo - is there a better way of doing this?
677:             $this->remove( $editor, array($parentId) );
678:             $this->create( $editor, $parentId, $data );
679:         }
680:     }
681: 
682: 
683:     /**
684:      * Delete rows
685:      *  @param Editor $editor Host Editor instance
686:      *  @param int[] $ids Parent row IDs to delete
687:      *  @internal
688:      */
689:     public function remove ( $editor, $ids )
690:     {
691:         if ( ! $this->_set ) {
692:             return;
693:         }
694: 
695:         $that = $this;
696:         $this->_prep( $editor );
697:         $db = $editor->db();
698:         
699:         if ( isset($this->_join['table']) ) {
700:             $stmt = $db
701:                 ->query( 'delete' )
702:                 ->table( $this->_join['table'] )
703:                 ->or_where( $this->_join['parent'][1], $ids )
704:                 ->exec();
705:         }
706:         else {
707:             $stmt = $db
708:                 ->query( 'delete' )
709:                 ->table( $this->_table )
710:                 ->where_group( function ( $q ) use ( $that, $ids ) {
711:                     $q->or_where( $that->_join['child'], $ids );
712:                 } );
713: 
714:             $this->_apply_where( $stmt );
715: 
716:             $stmt->exec();
717:         }
718:     }
719: 
720: 
721:     /**
722:      * Validate input data
723:      *
724:      * @param array $errors Errors array
725:      * @param Editor $editor Editor instance
726:      * @param string[] $data Data to validate
727:      * @internal
728:      */
729:     public function validate ( &$errors, $editor, $data )
730:     {
731:         if ( ! $this->_set || ! isset($data[$this->_name]) ) {
732:             return;
733:         }
734: 
735:         $this->_prep( $editor );
736: 
737:         $joinData = $data[$this->_name];
738: 
739:         if ( $this->_type === 'object' ) {
740:             $this->_validateFields( $errors, $editor, $joinData, $this->_name.'.' );
741:         }
742:         else {
743:             for ( $i=0 ; $i<count($joinData) ; $i++ ) {
744:                 $this->_validateFields( $errors, $editor, $joinData[$i], $this->_name.'[].' );
745:             }
746:         }
747:     }
748: 
749: 
750: 
751:     /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
752:      * Private methods
753:      */
754:     
755:     /**
756:      * Add local WHERE condition to query
757:      *  @param \DataTables\Database\Query $query Query instance to apply the WHERE conditions to
758:      *  @private
759:      */
760:     private function _apply_where ( $query )
761:     {
762:         for ( $i=0 ; $i<count($this->_where) ; $i++ ) {
763:             if ( is_callable( $this->_where[$i] ) ) {
764:                 $this->_where[$i]( $query );
765:             }
766:             else {
767:                 $query->where(
768:                     $this->_where[$i]['key'],
769:                     $this->_where[$i]['value'],
770:                     $this->_where[$i]['op']
771:                 );
772:             }
773:         }
774:     }
775: 
776: 
777:     /**
778:      * Create a row.
779:      *  @param \DataTables\Database $db Database reference to use
780:      *  @param int $parentId Parent row's primary key value
781:      *  @param string[] $data Data to be set for the join
782:      *  @private
783:      */
784:     private function _insert( $db, $parentId, $data )
785:     {
786:         if ( isset($this->_join['table']) ) {
787:             // Insert keys into the join table
788:             $stmt = $db
789:                 ->query('insert')
790:                 ->table( $this->_join['table'] )
791:                 ->set( $this->_join['parent'][1], $parentId )
792:                 ->set( $this->_join['child'][1], $data[$this->_join['child'][0]] )
793:                 ->exec();
794:         }
795:         else {
796:             // Insert values into the target table
797:             $stmt = $db
798:                 ->query('insert')
799:                 ->table( $this->_table )
800:                 ->set( $this->_join['child'], $parentId );
801: 
802:             for ( $i=0 ; $i<count($this->_fields) ; $i++ ) {
803:                 $field = $this->_fields[$i];
804: 
805:                 if ( $field->apply( 'set', $data ) ) { // TODO should be create or edit
806:                     $stmt->set( $field->dbField(), $field->val('set', $data) );
807:                 }
808:             }
809: 
810:             // If the where condition variables should also be added to the database
811:             // Note that `whereSet` is now deprecated
812:             if ( $this->_whereSet ) {
813:                 for ( $i=0, $ien=count($this->_where) ; $i<$ien ; $i++ ) {
814:                     if ( ! is_callable( $this->_where[$i] ) ) {
815:                         $stmt->set( $this->_where[$i]['key'], $this->_where[$i]['value'] );
816:                     }
817:                 }
818:             }
819: 
820:             $stmt->exec(); 
821:         }
822:     }
823: 
824: 
825:     /**
826:      * Prepare the instance to be run.
827:      *
828:      * @param  Editor $editor Editor instance
829:      * @private
830:      */
831:     private function _prep ( $editor )
832:     {
833:         $links = $this->_links;
834: 
835:         // Were links used to configure this instance - if so, we need to
836:         // back them onto the join array
837:         if ( $this->_join['parent'] === null && count($links) ) {
838:             $editorTable = $editor->table();
839:             $editorTable = $editorTable[0];
840:             $joinTable = $this->table();
841: 
842:             if ( $this->_aliasParentTable ) {
843:                 $editorTable = $this->_aliasParentTable;
844:             }
845: 
846:             if ( count( $links ) === 2 ) {
847:                 // No link table
848:                 $f1 = explode( '.', $links[0] );
849:                 $f2 = explode( '.', $links[1] );
850: 
851:                 if ( $f1[0] === $editorTable ) {
852:                     $this->_join['parent'] = $f1[1];
853:                     $this->_join['child'] = $f2[1];
854:                 }
855:                 else {
856:                     $this->_join['parent'] = $f2[1];
857:                     $this->_join['child'] = $f1[1];
858:                 }
859:             }
860:             else {
861:                 // Link table
862:                 $f1 = explode( '.', $links[0] );
863:                 $f2 = explode( '.', $links[1] );
864:                 $f3 = explode( '.', $links[2] );
865:                 $f4 = explode( '.', $links[3] );
866: 
867:                 // Discover the name of the link table
868:                 if ( $f1[0] !== $editorTable && $f1[0] !== $joinTable ) {
869:                     $this->_join['table'] = $f1[0];
870:                 }
871:                 else if ( $f2[0] !== $editorTable && $f2[0] !== $joinTable ) {
872:                     $this->_join['table'] = $f2[0];
873:                 }
874:                 else if ( $f3[0] !== $editorTable && $f3[0] !== $joinTable ) {
875:                     $this->_join['table'] = $f3[0];
876:                 }
877:                 else {
878:                     $this->_join['table'] = $f4[0];
879:                 }
880: 
881:                 $this->_join['parent'] = array( $f1[1], $f2[1] );
882:                 $this->_join['child'] = array( $f3[1], $f4[1] );
883:             }
884:         }
885:     }
886: 
887: 
888:     /**
889:      * Update a row.
890:      *  @param \DataTables\Database $db Database reference to use
891:      *  @param int $parentId Parent row's primary key value
892:      *  @param string[] $data Data to be set for the join
893:      *  @private
894:      */
895:     private function _update_row ( $db, $parentId, $data )
896:     {
897:         if ( isset($this->_join['table']) ) {
898:             // Got a link table, just insert the pkey references
899:             $db->push(
900:                 $this->_join['table'],
901:                 array(
902:                     $this->_join['parent'][1] => $parentId,
903:                     $this->_join['child'][1]  => $data[$this->_join['child'][0]]
904:                 ),
905:                 array(
906:                     $this->_join['parent'][1] => $parentId
907:                 )
908:             );
909:         }
910:         else {
911:             // No link table, just a direct reference
912:             $set = array(
913:                 $this->_join['child'] => $parentId
914:             );
915: 
916:             for ( $i=0 ; $i<count($this->_fields) ; $i++ ) {
917:                 $field = $this->_fields[$i];
918: 
919:                 if ( $field->apply( 'set', $data ) ) {
920:                     $set[ $field->dbField() ] = $field->val('set', $data);
921:                 }
922:             }
923: 
924:             // Add WHERE conditions
925:             $where = array($this->_join['child'] => $parentId);
926:             for ( $i=0, $ien=count($this->_where) ; $i<$ien ; $i++ ) {
927:                 $where[ $this->_where[$i]['key'] ] = $this->_where[$i]['value'];
928: 
929:                 // Is there any point in this? Is there any harm?
930:                 // Note that `whereSet` is now deprecated
931:                 if ( $this->_whereSet ) {
932:                     if ( ! is_callable( $this->_where[$i] ) ) {
933:                         $set[ $this->_where[$i]['key'] ] = $this->_where[$i]['value'];
934:                     }
935:                 }
936:             }
937: 
938:             $db->push( $this->_table, $set, $where );
939:         }
940:     }
941: 
942: 
943:     /**
944:      * Create an SQL string from the fields that this instance knows about for
945:      * using in queries
946:      *  @param string $direction Direction: 'get' or 'set'.
947:      *  @returns array Fields to include
948:      *  @private
949:      */
950:     private function _fields ( $direction )
951:     {
952:         $fields = array();
953: 
954:         for ( $i=0 ; $i<count($this->_fields) ; $i++ ) {
955:             $field = $this->_fields[$i];
956: 
957:             if ( $field->apply( $direction, null ) ) {
958:                 if ( strpos( $field->dbField() , "." ) === false ) {
959:                     $fields[] = $this->_table.'.'.$field->dbField() ." as ".$field->dbField();;
960:                 }
961:                 else {
962:                     $fields[] = $field->dbField();// ." as ".$field->dbField();
963:                 }
964:             }
965:         }
966: 
967:         return $fields;
968:     }
969: 
970: 
971:     /**
972:      * Validate input data
973:      *
974:      * @param array $errors Errors array
975:      * @param Editor $editor Editor instance
976:      * @param string[] $data Data to validate
977:      * @param string $prefix Field error prefix for client-side to show the
978:      *   error message on the appropriate field
979:      * @internal
980:      */
981:     private function _validateFields ( &$errors, $editor, $data, $prefix )
982:     {
983:         for ( $i=0 ; $i<count($this->_fields) ; $i++ ) {
984:             $field = $this->_fields[$i];
985:             $validation = $field->validate( $data, $editor );
986: 
987:             if ( $validation !== true ) {
988:                 $errors[] = array(
989:                     "name" => $prefix.$field->name(),
990:                     "status" => $validation
991:                 );
992:             }
993:         }
994:     }
995: }
996: 
997: 
DataTables Editor 1.8.0 - PHP libraries API documentation generated by ApiGen