Index: trunk/phase3/includes/DBDataObject.php |
— | — | @@ -54,6 +54,12 @@ |
55 | 55 | protected $fields = array( 'id' => null ); |
56 | 56 | |
57 | 57 | /** |
| 58 | + * @since 1.20 |
| 59 | + * @var DBTable |
| 60 | + */ |
| 61 | + protected $table; |
| 62 | + |
| 63 | + /** |
58 | 64 | * If the object should update summaries of linked items when changed. |
59 | 65 | * For example, update the course_count field in universities when a course in courses is deleted. |
60 | 66 | * Settings this to false can prevent needless updating work in situations |
— | — | @@ -73,7 +79,6 @@ |
74 | 80 | * @var bool |
75 | 81 | */ |
76 | 82 | protected $inSummaryMode = false; |
77 | | - |
78 | 83 | |
79 | 84 | /** |
80 | 85 | * The database connection to use for read operations. |
— | — | @@ -82,81 +87,9 @@ |
83 | 88 | * @since 1.20 |
84 | 89 | * @var integer DB_ enum |
85 | 90 | */ |
86 | | - protected static $readDb = DB_SLAVE; |
87 | | - |
88 | | - /** |
89 | | - * Returns the name of the database table objects of this type are stored in. |
90 | | - * |
91 | | - * @since 1.20 |
92 | | - * |
93 | | - * @throws MWException |
94 | | - * @return string |
95 | | - */ |
96 | | - public static function getDBTable() { |
97 | | - throw new MWException( 'Class "' . get_called_class() . '" did not implement getDBTable' ); |
98 | | - } |
99 | | - |
100 | | - /** |
101 | | - * Gets the db field prefix. |
102 | | - * |
103 | | - * @since 1.20 |
104 | | - * |
105 | | - * @throws MWException |
106 | | - * @return string |
107 | | - */ |
108 | | - protected static function getFieldPrefix() { |
109 | | - throw new MWException( 'Class "' . get_called_class() . '" did not implement getFieldPrefix' ); |
110 | | - } |
111 | | - |
112 | | - /** |
113 | | - * Returns an array with the fields and their types this object contains. |
114 | | - * This corresponds directly to the fields in the database, without prefix. |
115 | | - * |
116 | | - * field name => type |
117 | | - * |
118 | | - * Allowed types: |
119 | | - * * id |
120 | | - * * str |
121 | | - * * int |
122 | | - * * float |
123 | | - * * bool |
124 | | - * * array |
125 | | - * |
126 | | - * @since 1.20 |
127 | | - * |
128 | | - * @throws MWException |
129 | | - * @return array |
130 | | - */ |
131 | | - protected static function getFieldTypes() { |
132 | | - throw new MWException( 'Class did not implement getFieldTypes' ); |
133 | | - } |
134 | | - |
135 | | - /** |
136 | | - * Returns a list of default field values. |
137 | | - * field name => field value |
138 | | - * |
139 | | - * @since 1.20 |
140 | | - * |
141 | | - * @return array |
142 | | - */ |
143 | | - public static function getDefaults() { |
144 | | - return array(); |
145 | | - } |
| 91 | + protected $readDb = DB_SLAVE; |
146 | 92 | |
147 | 93 | /** |
148 | | - * Returns a list of the summary fields. |
149 | | - * These are fields that cache computed values, such as the amount of linked objects of $type. |
150 | | - * This is relevant as one might not want to do actions such as log changes when these get updated. |
151 | | - * |
152 | | - * @since 1.20 |
153 | | - * |
154 | | - * @return array |
155 | | - */ |
156 | | - public static function getSummaryFields() { |
157 | | - return array(); |
158 | | - } |
159 | | - |
160 | | - /** |
161 | 94 | * Constructor. |
162 | 95 | * |
163 | 96 | * @since 1.20 |
— | — | @@ -164,13 +97,15 @@ |
165 | 98 | * @param array|null $fields |
166 | 99 | * @param boolean $loadDefaults |
167 | 100 | */ |
168 | | - public function __construct( $fields = null, $loadDefaults = false ) { |
| 101 | + public function __construct( DBTable $table, $fields = null, $loadDefaults = false ) { |
| 102 | + $this->table = $table; |
| 103 | + |
169 | 104 | if ( !is_array( $fields ) ) { |
170 | 105 | $fields = array(); |
171 | 106 | } |
172 | 107 | |
173 | 108 | if ( $loadDefaults ) { |
174 | | - $fields = array_merge( $this->getDefaults(), $fields ); |
| 109 | + $fields = array_merge( $this->table->getDefaults(), $fields ); |
175 | 110 | } |
176 | 111 | |
177 | 112 | $this->setFields( $fields ); |
— | — | @@ -549,7 +484,7 @@ |
550 | 485 | * @throws MWException |
551 | 486 | */ |
552 | 487 | public function setField( $name, $value ) { |
553 | | - $fields = $this->getFieldTypes(); |
| 488 | + $fields = $this->table->getFieldTypes(); |
554 | 489 | |
555 | 490 | if ( array_key_exists( $name, $fields ) ) { |
556 | 491 | switch ( $fields[$name] ) { |
— | — | @@ -603,18 +538,19 @@ |
604 | 539 | * |
605 | 540 | * @return DBDataObject |
606 | 541 | */ |
607 | | - public static function newFromArray( array $data, $loadDefaults = false ) { |
608 | | - return new static( $data, $loadDefaults ); |
| 542 | + public function newFromArray( array $data, $loadDefaults = false ) { |
| 543 | + return new self( $data, $loadDefaults ); |
609 | 544 | } |
610 | 545 | |
611 | 546 | /** |
612 | 547 | * Get the database type used for read operations. |
613 | 548 | * |
614 | 549 | * @since 1.20 |
| 550 | + * |
615 | 551 | * @return integer DB_ enum |
616 | 552 | */ |
617 | | - public static function getReadDb() { |
618 | | - return self::$readDb; |
| 553 | + public function getReadDb() { |
| 554 | + return $this->readDb; |
619 | 555 | } |
620 | 556 | |
621 | 557 | /** |
— | — | @@ -624,8 +560,8 @@ |
625 | 561 | * |
626 | 562 | * @since 1.20 |
627 | 563 | */ |
628 | | - public static function setReadDb( $db ) { |
629 | | - self::$readDb = $db; |
| 564 | + public function setReadDb( $db ) { |
| 565 | + $this->readDb = $db; |
630 | 566 | } |
631 | 567 | |
632 | 568 | /** |
— | — | @@ -637,156 +573,11 @@ |
638 | 574 | * |
639 | 575 | * @return boolean |
640 | 576 | */ |
641 | | - public static function canHaveField( $name ) { |
642 | | - return array_key_exists( $name, static::getFieldTypes() ); |
| 577 | + public function canHaveField( $name ) { |
| 578 | + return array_key_exists( $name, $this->table->getFieldTypes() ); |
643 | 579 | } |
644 | 580 | |
645 | 581 | /** |
646 | | - * Takes in a field or array of fields and returns an |
647 | | - * array with their prefixed versions, ready for db usage. |
648 | | - * |
649 | | - * @since 1.20 |
650 | | - * |
651 | | - * @param array|string $fields |
652 | | - * |
653 | | - * @return array |
654 | | - */ |
655 | | - public static function getPrefixedFields( array $fields ) { |
656 | | - foreach ( $fields as &$field ) { |
657 | | - $field = static::getPrefixedField( $field ); |
658 | | - } |
659 | | - |
660 | | - return $fields; |
661 | | - } |
662 | | - |
663 | | - /** |
664 | | - * Takes in a field and returns an it's prefixed version, ready for db usage. |
665 | | - * |
666 | | - * @since 1.20 |
667 | | - * |
668 | | - * @param string|array $field |
669 | | - * |
670 | | - * @return string |
671 | | - * @throws MWException |
672 | | - */ |
673 | | - public static function getPrefixedField( $field ) { |
674 | | - return static::getFieldPrefix() . $field; |
675 | | - } |
676 | | - |
677 | | - /** |
678 | | - * Takes in an associative array with field names as keys and |
679 | | - * their values as value. The field names are prefixed with the |
680 | | - * db field prefix. |
681 | | - * |
682 | | - * Field names can also be provided as an array with as first element a table name, such as |
683 | | - * $conditions = array( |
684 | | - * array( array( 'tablename', 'fieldname' ), $value ), |
685 | | - * ); |
686 | | - * |
687 | | - * @since 1.20 |
688 | | - * |
689 | | - * @param array $values |
690 | | - * |
691 | | - * @return array |
692 | | - */ |
693 | | - public static function getPrefixedValues( array $values ) { |
694 | | - $prefixedValues = array(); |
695 | | - |
696 | | - foreach ( $values as $field => $value ) { |
697 | | - if ( is_integer( $field ) ) { |
698 | | - if ( is_array( $value ) ) { |
699 | | - $field = $value[0]; |
700 | | - $value = $value[1]; |
701 | | - } |
702 | | - else { |
703 | | - $value = explode( ' ', $value, 2 ); |
704 | | - $value[0] = static::getPrefixedField( $value[0] ); |
705 | | - $prefixedValues[] = implode( ' ', $value ); |
706 | | - continue; |
707 | | - } |
708 | | - } |
709 | | - |
710 | | - $prefixedValues[static::getPrefixedField( $field )] = $value; |
711 | | - } |
712 | | - |
713 | | - return $prefixedValues; |
714 | | - } |
715 | | - |
716 | | - /** |
717 | | - * Get an array with fields from a database result, |
718 | | - * that can be fed directly to the constructor or |
719 | | - * to setFields. |
720 | | - * |
721 | | - * @since 1.20 |
722 | | - * |
723 | | - * @param object $result |
724 | | - * |
725 | | - * @return array |
726 | | - */ |
727 | | - public static function getFieldsFromDBResult( $result ) { |
728 | | - $result = (array)$result; |
729 | | - return array_combine( |
730 | | - static::unprefixFieldNames( array_keys( $result ) ), |
731 | | - array_values( $result ) |
732 | | - ); |
733 | | - } |
734 | | - |
735 | | - /** |
736 | | - * Takes a field name with prefix and returns the unprefixed equivalent. |
737 | | - * |
738 | | - * @since 1.20 |
739 | | - * |
740 | | - * @param string $fieldName |
741 | | - * |
742 | | - * @return string |
743 | | - */ |
744 | | - public static function unprefixFieldName( $fieldName ) { |
745 | | - return substr( $fieldName, strlen( static::getFieldPrefix() ) ); |
746 | | - } |
747 | | - |
748 | | - /** |
749 | | - * Takes an array of field names with prefix and returns the unprefixed equivalent. |
750 | | - * |
751 | | - * @since 1.20 |
752 | | - * |
753 | | - * @param array $fieldNames |
754 | | - * |
755 | | - * @return array |
756 | | - */ |
757 | | - public static function unprefixFieldNames( array $fieldNames ) { |
758 | | - return array_map( 'static::unprefixFieldName', $fieldNames ); |
759 | | - } |
760 | | - |
761 | | - /** |
762 | | - * Get a new instance of the class from a database result. |
763 | | - * |
764 | | - * @since 1.20 |
765 | | - * |
766 | | - * @param stdClass $result |
767 | | - * |
768 | | - * @return DBDataObject |
769 | | - */ |
770 | | - public static function newFromDBResult( stdClass $result ) { |
771 | | - return static::newFromArray( static::getFieldsFromDBResult( $result ) ); |
772 | | - } |
773 | | - |
774 | | - /** |
775 | | - * Removes the object from the database. |
776 | | - * |
777 | | - * @since 1.20 |
778 | | - * |
779 | | - * @param array $conditions |
780 | | - * |
781 | | - * @return boolean Success indicator |
782 | | - */ |
783 | | - public static function delete( array $conditions ) { |
784 | | - return wfGetDB( DB_MASTER )->delete( |
785 | | - static::getDBTable(), |
786 | | - static::getPrefixedValues( $conditions ) |
787 | | - ); |
788 | | - } |
789 | | - |
790 | | - /** |
791 | 582 | * Add an amount (can be negative) to the specified field (needs to be numeric). |
792 | 583 | * |
793 | 584 | * @since 1.20 |
— | — | @@ -796,12 +587,12 @@ |
797 | 588 | * |
798 | 589 | * @return boolean Success indicator |
799 | 590 | */ |
800 | | - public static function addToField( $field, $amount ) { |
| 591 | + public function addToField( $field, $amount ) { |
801 | 592 | if ( $amount == 0 ) { |
802 | 593 | return true; |
803 | 594 | } |
804 | 595 | |
805 | | - if ( !static::hasIdField() ) { |
| 596 | + if ( !$this->hasIdField() ) { |
806 | 597 | return false; |
807 | 598 | } |
808 | 599 | |
— | — | @@ -810,12 +601,12 @@ |
811 | 602 | |
812 | 603 | $dbw = wfGetDB( DB_MASTER ); |
813 | 604 | |
814 | | - $fullField = static::getPrefixedField( $field ); |
| 605 | + $fullField = $this->getPrefixedField( $field ); |
815 | 606 | |
816 | 607 | $success = $dbw->update( |
817 | | - static::getDBTable(), |
| 608 | + $this->getDBTable(), |
818 | 609 | array( "$fullField=$fullField" . ( $isNegative ? '-' : '+' ) . $absoluteAmount ), |
819 | | - array( static::getPrefixedField( 'id' ) => static::getId() ), |
| 610 | + array( $this->getPrefixedField( 'id' ) => $this->getId() ), |
820 | 611 | __METHOD__ |
821 | 612 | ); |
822 | 613 | |
— | — | @@ -828,171 +619,6 @@ |
829 | 620 | |
830 | 621 | /** |
831 | 622 | * Selects the the specified fields of the records matching the provided |
832 | | - * conditions and returns them as DBDataObject. Field names get prefixed. |
833 | | - * |
834 | | - * @since 1.20 |
835 | | - * |
836 | | - * @param array|string|null $fields |
837 | | - * @param array $conditions |
838 | | - * @param array $options |
839 | | - * |
840 | | - * @return array of self |
841 | | - */ |
842 | | - public static function select( $fields = null, array $conditions = array(), array $options = array() ) { |
843 | | - $result = static::selectFields( $fields, $conditions, $options, false ); |
844 | | - |
845 | | - $objects = array(); |
846 | | - |
847 | | - foreach ( $result as $record ) { |
848 | | - $objects[] = static::newFromArray( $record ); |
849 | | - } |
850 | | - |
851 | | - return $objects; |
852 | | - } |
853 | | - |
854 | | - /** |
855 | | - * Selects the the specified fields of the records matching the provided |
856 | | - * conditions and returns them as associative arrays. |
857 | | - * Provided field names get prefixed. |
858 | | - * Returned field names will not have a prefix. |
859 | | - * |
860 | | - * When $collapse is true: |
861 | | - * If one field is selected, each item in the result array will be this field. |
862 | | - * If two fields are selected, each item in the result array will have as key |
863 | | - * the first field and as value the second field. |
864 | | - * If more then two fields are selected, each item will be an associative array. |
865 | | - * |
866 | | - * @since 1.20 |
867 | | - * |
868 | | - * @param array|string|null $fields |
869 | | - * @param array $conditions |
870 | | - * @param array $options |
871 | | - * @param boolean $collapse Set to false to always return each result row as associative array. |
872 | | - * |
873 | | - * @return array of array |
874 | | - */ |
875 | | - public static function selectFields( $fields = null, array $conditions = array(), array $options = array(), $collapse = true ) { |
876 | | - if ( is_null( $fields ) ) { |
877 | | - $fields = array_keys( static::getFieldTypes() ); |
878 | | - } |
879 | | - else { |
880 | | - $fields = (array)$fields; |
881 | | - } |
882 | | - |
883 | | - $dbr = wfGetDB( static::getReadDb() ); |
884 | | - $result = $dbr->select( |
885 | | - static::getDBTable(), |
886 | | - static::getPrefixedFields( $fields ), |
887 | | - static::getPrefixedValues( $conditions ), |
888 | | - __METHOD__, |
889 | | - $options |
890 | | - ); |
891 | | - |
892 | | - $objects = array(); |
893 | | - |
894 | | - foreach ( $result as $record ) { |
895 | | - $objects[] = static::getFieldsFromDBResult( $record ); |
896 | | - } |
897 | | - |
898 | | - if ( $collapse ) { |
899 | | - if ( count( $fields ) === 1 ) { |
900 | | - $objects = array_map( 'array_shift', $objects ); |
901 | | - } |
902 | | - elseif ( count( $fields ) === 2 ) { |
903 | | - $o = array(); |
904 | | - |
905 | | - foreach ( $objects as $object ) { |
906 | | - $o[array_shift( $object )] = array_shift( $object ); |
907 | | - } |
908 | | - |
909 | | - $objects = $o; |
910 | | - } |
911 | | - } |
912 | | - |
913 | | - return $objects; |
914 | | - } |
915 | | - |
916 | | - /** |
917 | | - * Selects the the specified fields of the first matching record. |
918 | | - * Field names get prefixed. |
919 | | - * |
920 | | - * @since 1.20 |
921 | | - * |
922 | | - * @param array|string|null $fields |
923 | | - * @param array $conditions |
924 | | - * @param array $options |
925 | | - * |
926 | | - * @return DBObject|bool False on failure |
927 | | - */ |
928 | | - public static function selectRow( $fields = null, array $conditions = array(), array $options = array() ) { |
929 | | - $options['LIMIT'] = 1; |
930 | | - |
931 | | - $objects = static::select( $fields, $conditions, $options ); |
932 | | - |
933 | | - return count( $objects ) > 0 ? $objects[0] : false; |
934 | | - } |
935 | | - |
936 | | - /** |
937 | | - * Selects the the specified fields of the first record matching the provided |
938 | | - * conditions and returns it as an associative array, or false when nothing matches. |
939 | | - * This method makes use of selectFields and expects the same parameters and |
940 | | - * returns the same results (if there are any, if there are none, this method returns false). |
941 | | - * @see DBDataObject::selectFields |
942 | | - * |
943 | | - * @since 1.20 |
944 | | - * |
945 | | - * @param array|string|null $fields |
946 | | - * @param array $conditions |
947 | | - * @param array $options |
948 | | - * @param boolean $collapse Set to false to always return each result row as associative array. |
949 | | - * |
950 | | - * @return mixed|array|bool False on failure |
951 | | - */ |
952 | | - public static function selectFieldsRow( $fields = null, array $conditions = array(), array $options = array(), $collapse = true ) { |
953 | | - $options['LIMIT'] = 1; |
954 | | - |
955 | | - $objects = static::selectFields( $fields, $conditions, $options, $collapse ); |
956 | | - |
957 | | - return count( $objects ) > 0 ? $objects[0] : false; |
958 | | - } |
959 | | - |
960 | | - /** |
961 | | - * Returns if there is at least one record matching the provided conditions. |
962 | | - * Condition field names get prefixed. |
963 | | - * |
964 | | - * @since 1.20 |
965 | | - * |
966 | | - * @param array $conditions |
967 | | - * |
968 | | - * @return boolean |
969 | | - */ |
970 | | - public static function has( array $conditions = array() ) { |
971 | | - return static::selectRow( array( 'id' ), $conditions ) !== false; |
972 | | - } |
973 | | - |
974 | | - /** |
975 | | - * Returns the amount of matching records. |
976 | | - * Condition field names get prefixed. |
977 | | - * |
978 | | - * @since 1.20 |
979 | | - * |
980 | | - * @param array $conditions |
981 | | - * @param array $options |
982 | | - * |
983 | | - * @return integer |
984 | | - */ |
985 | | - public static function count( array $conditions = array(), array $options = array() ) { |
986 | | - $res = static::rawSelectRow( |
987 | | - array( 'COUNT(*) AS rowcount' ), |
988 | | - static::getPrefixedValues( $conditions ), |
989 | | - $options |
990 | | - ); |
991 | | - |
992 | | - return $res->rowcount; |
993 | | - } |
994 | | - |
995 | | - /** |
996 | | - * Selects the the specified fields of the records matching the provided |
997 | 623 | * conditions. Field names do NOT get prefixed. |
998 | 624 | * |
999 | 625 | * @since 1.20 |
— | — | @@ -1016,102 +642,17 @@ |
1017 | 643 | } |
1018 | 644 | |
1019 | 645 | /** |
1020 | | - * Update the records matching the provided conditions by |
1021 | | - * setting the fields that are keys in the $values param to |
1022 | | - * their corresponding values. |
1023 | | - * |
1024 | | - * @since 1.20 |
1025 | | - * |
1026 | | - * @param array $values |
1027 | | - * @param array $conditions |
1028 | | - * |
1029 | | - * @return boolean Success indicator |
1030 | | - */ |
1031 | | - public static function update( array $values, array $conditions = array() ) { |
1032 | | - $dbw = wfGetDB( DB_MASTER ); |
1033 | | - |
1034 | | - return $dbw->update( |
1035 | | - static::getDBTable(), |
1036 | | - static::getPrefixedValues( $values ), |
1037 | | - static::getPrefixedValues( $conditions ), |
1038 | | - __METHOD__ |
1039 | | - ); |
1040 | | - } |
1041 | | - |
1042 | | - /** |
1043 | 646 | * Return the names of the fields. |
1044 | 647 | * |
1045 | 648 | * @since 1.20 |
1046 | 649 | * |
1047 | 650 | * @return array |
1048 | 651 | */ |
1049 | | - public static function getFieldNames() { |
1050 | | - return array_keys( static::getFieldTypes() ); |
| 652 | + public function getFieldNames() { |
| 653 | + return array_keys( $this->table->getFieldTypes() ); |
1051 | 654 | } |
1052 | 655 | |
1053 | 656 | /** |
1054 | | - * Returns an array with the fields and their descriptions. |
1055 | | - * |
1056 | | - * field name => field description |
1057 | | - * |
1058 | | - * @since 1.20 |
1059 | | - * |
1060 | | - * @return array |
1061 | | - */ |
1062 | | - public static function getFieldDescriptions() { |
1063 | | - return array(); |
1064 | | - } |
1065 | | - |
1066 | | - /** |
1067 | | - * Get API parameters for the fields supported by this object. |
1068 | | - * |
1069 | | - * @since 1.20 |
1070 | | - * |
1071 | | - * @param boolean $requireParams |
1072 | | - * @param boolean $setDefaults |
1073 | | - * |
1074 | | - * @return array |
1075 | | - */ |
1076 | | - public static function getAPIParams( $requireParams = false, $setDefaults = false ) { |
1077 | | - $typeMap = array( |
1078 | | - 'id' => 'integer', |
1079 | | - 'int' => 'integer', |
1080 | | - 'float' => 'NULL', |
1081 | | - 'str' => 'string', |
1082 | | - 'bool' => 'integer', |
1083 | | - 'array' => 'string', |
1084 | | - 'blob' => 'string', |
1085 | | - ); |
1086 | | - |
1087 | | - $params = array(); |
1088 | | - $defaults = static::getDefaults(); |
1089 | | - |
1090 | | - foreach ( static::getFieldTypes() as $field => $type ) { |
1091 | | - if ( $field == 'id' ) { |
1092 | | - continue; |
1093 | | - } |
1094 | | - |
1095 | | - $hasDefault = array_key_exists( $field, $defaults ); |
1096 | | - |
1097 | | - $params[$field] = array( |
1098 | | - ApiBase::PARAM_TYPE => $typeMap[$type], |
1099 | | - ApiBase::PARAM_REQUIRED => $requireParams && !$hasDefault |
1100 | | - ); |
1101 | | - |
1102 | | - if ( $type == 'array' ) { |
1103 | | - $params[$field][ApiBase::PARAM_ISMULTI] = true; |
1104 | | - } |
1105 | | - |
1106 | | - if ( $setDefaults && $hasDefault ) { |
1107 | | - $default = is_array( $defaults[$field] ) ? implode( '|', $defaults[$field] ) : $defaults[$field]; |
1108 | | - $params[$field][ApiBase::PARAM_DFLT] = $default; |
1109 | | - } |
1110 | | - } |
1111 | | - |
1112 | | - return $params; |
1113 | | - } |
1114 | | - |
1115 | | - /** |
1116 | 657 | * Computes and updates the values of the summary fields. |
1117 | 658 | * |
1118 | 659 | * @since 1.20 |
— | — | @@ -1123,26 +664,6 @@ |
1124 | 665 | } |
1125 | 666 | |
1126 | 667 | /** |
1127 | | - * Computes the values of the summary fields of the objects matching the provided conditions. |
1128 | | - * |
1129 | | - * @since 1.20 |
1130 | | - * |
1131 | | - * @param array|string|null $summaryFields |
1132 | | - * @param array $conditions |
1133 | | - */ |
1134 | | - public static function updateSummaryFields( $summaryFields = null, array $conditions = array() ) { |
1135 | | - self::setReadDb( DB_MASTER ); |
1136 | | - |
1137 | | - foreach ( self::select( null, $conditions ) as /* DBDataObject */ $item ) { |
1138 | | - $item->loadSummaryFields( $summaryFields ); |
1139 | | - $item->setSummaryMode( true ); |
1140 | | - $item->saveExisting(); |
1141 | | - } |
1142 | | - |
1143 | | - self::setReadDb( DB_SLAVE ); |
1144 | | - } |
1145 | | - |
1146 | | - /** |
1147 | 668 | * Sets the value for the @see $updateSummaries field. |
1148 | 669 | * |
1149 | 670 | * @since 1.20 |
— | — | @@ -1185,5 +706,52 @@ |
1186 | 707 | |
1187 | 708 | return false; |
1188 | 709 | } |
| 710 | + |
| 711 | + protected function getDBTable() { |
| 712 | + return $this->table->getDBTable(); |
| 713 | + } |
| 714 | + |
| 715 | + /** |
| 716 | + * Get an array with fields from a database result, |
| 717 | + * that can be fed directly to the constructor or |
| 718 | + * to setFields. |
| 719 | + * |
| 720 | + * @since 1.20 |
| 721 | + * |
| 722 | + * @param object $result |
| 723 | + * |
| 724 | + * @return array |
| 725 | + */ |
| 726 | + public function getFieldsFromDBResult( $result ) { |
| 727 | + $result = (array)$result; |
| 728 | + return array_combine( |
| 729 | + $this->unprefixFieldNames( array_keys( $result ) ), |
| 730 | + array_values( $result ) |
| 731 | + ); |
| 732 | + } |
| 733 | + |
| 734 | + /** |
| 735 | + * Get a new instance of the class from a database result. |
| 736 | + * |
| 737 | + * @since 1.20 |
| 738 | + * |
| 739 | + * @param stdClass $result |
| 740 | + * |
| 741 | + * @return DBDataObject |
| 742 | + */ |
| 743 | + public function newFromDBResult( stdClass $result ) { |
| 744 | + return $this->newFromArray( $this->getFieldsFromDBResult( $result ) ); |
| 745 | + } |
| 746 | + |
| 747 | + /** |
| 748 | + * Returns the table this DBDataObject is a row in. |
| 749 | + * |
| 750 | + * @since 1.20 |
| 751 | + * |
| 752 | + * @return DBTable |
| 753 | + */ |
| 754 | + public function getTable() { |
| 755 | + return $this->table; |
| 756 | + } |
1189 | 757 | |
1190 | 758 | } |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -51,6 +51,7 @@ |
52 | 52 | 'CookieJar' => 'includes/Cookie.php', |
53 | 53 | 'CurlHttpRequest' => 'includes/HttpFunctions.php', |
54 | 54 | 'DBDataObject' => 'includes/DBDataObject.php', |
| 55 | + 'DBTable' => 'includes/DBTable.php', |
55 | 56 | 'DeferrableUpdate' => 'includes/DeferredUpdates.php', |
56 | 57 | 'DeferredUpdates' => 'includes/DeferredUpdates.php', |
57 | 58 | 'DeprecatedGlobal' => 'includes/DeprecatedGlobal.php', |
Index: trunk/phase3/includes/DBTable.php |
— | — | @@ -0,0 +1,522 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Abstract base class for representing a single database table. |
| 6 | + * |
| 7 | + * @since 1.20 |
| 8 | + * |
| 9 | + * @file DBTable.php |
| 10 | + * |
| 11 | + * @licence GNU GPL v2 or later |
| 12 | + * @author Jeroen De Dauw < jeroendedauw@gmail.com > |
| 13 | + */ |
| 14 | +abstract class DBTable { |
| 15 | + |
| 16 | + /** |
| 17 | + * Returns the name of the database table objects of this type are stored in. |
| 18 | + * |
| 19 | + * @since 1.20 |
| 20 | + * |
| 21 | + * @return string |
| 22 | + */ |
| 23 | + public abstract function getDBTable(); |
| 24 | + |
| 25 | + /** |
| 26 | + * Returns the name of a DBDataObject deriving class that |
| 27 | + * represents single rows in this table. |
| 28 | + * |
| 29 | + * @since 1.20 |
| 30 | + * |
| 31 | + * @return string |
| 32 | + */ |
| 33 | + protected abstract function getDataObjectClass(); |
| 34 | + |
| 35 | + /** |
| 36 | + * Gets the db field prefix. |
| 37 | + * |
| 38 | + * @since 1.20 |
| 39 | + * |
| 40 | + * @return string |
| 41 | + */ |
| 42 | + protected abstract function getFieldPrefix(); |
| 43 | + |
| 44 | + /** |
| 45 | + * Returns an array with the fields and their types this object contains. |
| 46 | + * This corresponds directly to the fields in the database, without prefix. |
| 47 | + * |
| 48 | + * field name => type |
| 49 | + * |
| 50 | + * Allowed types: |
| 51 | + * * id |
| 52 | + * * str |
| 53 | + * * int |
| 54 | + * * float |
| 55 | + * * bool |
| 56 | + * * array |
| 57 | + * |
| 58 | + * @since 1.20 |
| 59 | + * |
| 60 | + * @return array |
| 61 | + */ |
| 62 | + public abstract function getFieldTypes(); |
| 63 | + |
| 64 | + /** |
| 65 | + * The database connection to use for read operations. |
| 66 | + * Can be changed via @see setReadDb. |
| 67 | + * |
| 68 | + * @since 1.20 |
| 69 | + * @var integer DB_ enum |
| 70 | + */ |
| 71 | + protected $readDb = DB_SLAVE; |
| 72 | + |
| 73 | + /** |
| 74 | + * Returns a list of default field values. |
| 75 | + * field name => field value |
| 76 | + * |
| 77 | + * @since 1.20 |
| 78 | + * |
| 79 | + * @return array |
| 80 | + */ |
| 81 | + public function getDefaults() { |
| 82 | + return array(); |
| 83 | + } |
| 84 | + |
| 85 | + /** |
| 86 | + * Returns a list of the summary fields. |
| 87 | + * These are fields that cache computed values, such as the amount of linked objects of $type. |
| 88 | + * This is relevant as one might not want to do actions such as log changes when these get updated. |
| 89 | + * |
| 90 | + * @since 1.20 |
| 91 | + * |
| 92 | + * @return array |
| 93 | + */ |
| 94 | + public function getSummaryFields() { |
| 95 | + return array(); |
| 96 | + } |
| 97 | + |
| 98 | + /** |
| 99 | + * Selects the the specified fields of the records matching the provided |
| 100 | + * conditions and returns them as DBDataObject. Field names get prefixed. |
| 101 | + * |
| 102 | + * @since 1.20 |
| 103 | + * |
| 104 | + * @param array|string|null $fields |
| 105 | + * @param array $conditions |
| 106 | + * @param array $options |
| 107 | + * |
| 108 | + * @return array of self |
| 109 | + */ |
| 110 | + public function select( $fields = null, array $conditions = array(), array $options = array() ) { |
| 111 | + $result = $this->selectFields( $fields, $conditions, $options, false ); |
| 112 | + |
| 113 | + $objects = array(); |
| 114 | + |
| 115 | + foreach ( $result as $record ) { |
| 116 | + $objects[] = $this->newFromArray( $record ); |
| 117 | + } |
| 118 | + |
| 119 | + return $objects; |
| 120 | + } |
| 121 | + |
| 122 | + /** |
| 123 | + * Selects the the specified fields of the records matching the provided |
| 124 | + * conditions and returns them as associative arrays. |
| 125 | + * Provided field names get prefixed. |
| 126 | + * Returned field names will not have a prefix. |
| 127 | + * |
| 128 | + * When $collapse is true: |
| 129 | + * If one field is selected, each item in the result array will be this field. |
| 130 | + * If two fields are selected, each item in the result array will have as key |
| 131 | + * the first field and as value the second field. |
| 132 | + * If more then two fields are selected, each item will be an associative array. |
| 133 | + * |
| 134 | + * @since 1.20 |
| 135 | + * |
| 136 | + * @param array|string|null $fields |
| 137 | + * @param array $conditions |
| 138 | + * @param array $options |
| 139 | + * @param boolean $collapse Set to false to always return each result row as associative array. |
| 140 | + * |
| 141 | + * @return array of array |
| 142 | + */ |
| 143 | + public function selectFields( $fields = null, array $conditions = array(), array $options = array(), $collapse = true ) { |
| 144 | + if ( is_null( $fields ) ) { |
| 145 | + $fields = array_keys( $this->getFieldTypes() ); |
| 146 | + } |
| 147 | + else { |
| 148 | + $fields = (array)$fields; |
| 149 | + } |
| 150 | + |
| 151 | + $dbr = wfGetDB( $this->getReadDb() ); |
| 152 | + $result = $dbr->select( |
| 153 | + $this->getDBTable(), |
| 154 | + $this->getPrefixedFields( $fields ), |
| 155 | + $this->getPrefixedValues( $conditions ), |
| 156 | + __METHOD__, |
| 157 | + $options |
| 158 | + ); |
| 159 | + |
| 160 | + $objects = array(); |
| 161 | + |
| 162 | + foreach ( $result as $record ) { |
| 163 | + $objects[] = $this->getFieldsFromDBResult( $record ); |
| 164 | + } |
| 165 | + |
| 166 | + if ( $collapse ) { |
| 167 | + if ( count( $fields ) === 1 ) { |
| 168 | + $objects = array_map( 'array_shift', $objects ); |
| 169 | + } |
| 170 | + elseif ( count( $fields ) === 2 ) { |
| 171 | + $o = array(); |
| 172 | + |
| 173 | + foreach ( $objects as $object ) { |
| 174 | + $o[array_shift( $object )] = array_shift( $object ); |
| 175 | + } |
| 176 | + |
| 177 | + $objects = $o; |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + return $objects; |
| 182 | + } |
| 183 | + |
| 184 | + /** |
| 185 | + * Selects the the specified fields of the first matching record. |
| 186 | + * Field names get prefixed. |
| 187 | + * |
| 188 | + * @since 1.20 |
| 189 | + * |
| 190 | + * @param array|string|null $fields |
| 191 | + * @param array $conditions |
| 192 | + * @param array $options |
| 193 | + * |
| 194 | + * @return DBObject|bool False on failure |
| 195 | + */ |
| 196 | + public function selectRow( $fields = null, array $conditions = array(), array $options = array() ) { |
| 197 | + $options['LIMIT'] = 1; |
| 198 | + |
| 199 | + $objects = $this->select( $fields, $conditions, $options ); |
| 200 | + |
| 201 | + return count( $objects ) > 0 ? $objects[0] : false; |
| 202 | + } |
| 203 | + |
| 204 | + /** |
| 205 | + * Selects the the specified fields of the first record matching the provided |
| 206 | + * conditions and returns it as an associative array, or false when nothing matches. |
| 207 | + * This method makes use of selectFields and expects the same parameters and |
| 208 | + * returns the same results (if there are any, if there are none, this method returns false). |
| 209 | + * @see DBDataObject::selectFields |
| 210 | + * |
| 211 | + * @since 1.20 |
| 212 | + * |
| 213 | + * @param array|string|null $fields |
| 214 | + * @param array $conditions |
| 215 | + * @param array $options |
| 216 | + * @param boolean $collapse Set to false to always return each result row as associative array. |
| 217 | + * |
| 218 | + * @return mixed|array|bool False on failure |
| 219 | + */ |
| 220 | + public function selectFieldsRow( $fields = null, array $conditions = array(), array $options = array(), $collapse = true ) { |
| 221 | + $options['LIMIT'] = 1; |
| 222 | + |
| 223 | + $objects = $this->selectFields( $fields, $conditions, $options, $collapse ); |
| 224 | + |
| 225 | + return count( $objects ) > 0 ? $objects[0] : false; |
| 226 | + } |
| 227 | + |
| 228 | + /** |
| 229 | + * Returns if there is at least one record matching the provided conditions. |
| 230 | + * Condition field names get prefixed. |
| 231 | + * |
| 232 | + * @since 1.20 |
| 233 | + * |
| 234 | + * @param array $conditions |
| 235 | + * |
| 236 | + * @return boolean |
| 237 | + */ |
| 238 | + public function has( array $conditions = array() ) { |
| 239 | + return $this->selectRow( array( 'id' ), $conditions ) !== false; |
| 240 | + } |
| 241 | + |
| 242 | + /** |
| 243 | + * Returns the amount of matching records. |
| 244 | + * Condition field names get prefixed. |
| 245 | + * |
| 246 | + * @since 1.20 |
| 247 | + * |
| 248 | + * @param array $conditions |
| 249 | + * @param array $options |
| 250 | + * |
| 251 | + * @return integer |
| 252 | + */ |
| 253 | + public function count( array $conditions = array(), array $options = array() ) { |
| 254 | + $res = $this->rawSelectRow( |
| 255 | + array( 'COUNT(*) AS rowcount' ), |
| 256 | + $this->getPrefixedValues( $conditions ), |
| 257 | + $options |
| 258 | + ); |
| 259 | + |
| 260 | + return $res->rowcount; |
| 261 | + } |
| 262 | + |
| 263 | + /** |
| 264 | + * Removes the object from the database. |
| 265 | + * |
| 266 | + * @since 1.20 |
| 267 | + * |
| 268 | + * @param array $conditions |
| 269 | + * |
| 270 | + * @return boolean Success indicator |
| 271 | + */ |
| 272 | + public function delete( array $conditions ) { |
| 273 | + return wfGetDB( DB_MASTER )->delete( |
| 274 | + $this->getDBTable(), |
| 275 | + $this->getPrefixedValues( $conditions ) |
| 276 | + ); |
| 277 | + } |
| 278 | + |
| 279 | + /** |
| 280 | + * Get API parameters for the fields supported by this object. |
| 281 | + * |
| 282 | + * @since 1.20 |
| 283 | + * |
| 284 | + * @param boolean $requireParams |
| 285 | + * @param boolean $setDefaults |
| 286 | + * |
| 287 | + * @return array |
| 288 | + */ |
| 289 | + public function getAPIParams( $requireParams = false, $setDefaults = false ) { |
| 290 | + $typeMap = array( |
| 291 | + 'id' => 'integer', |
| 292 | + 'int' => 'integer', |
| 293 | + 'float' => 'NULL', |
| 294 | + 'str' => 'string', |
| 295 | + 'bool' => 'integer', |
| 296 | + 'array' => 'string', |
| 297 | + 'blob' => 'string', |
| 298 | + ); |
| 299 | + |
| 300 | + $params = array(); |
| 301 | + $defaults = $this->getDefaults(); |
| 302 | + |
| 303 | + foreach ( $this->getFieldTypes() as $field => $type ) { |
| 304 | + if ( $field == 'id' ) { |
| 305 | + continue; |
| 306 | + } |
| 307 | + |
| 308 | + $hasDefault = array_key_exists( $field, $defaults ); |
| 309 | + |
| 310 | + $params[$field] = array( |
| 311 | + ApiBase::PARAM_TYPE => $typeMap[$type], |
| 312 | + ApiBase::PARAM_REQUIRED => $requireParams && !$hasDefault |
| 313 | + ); |
| 314 | + |
| 315 | + if ( $type == 'array' ) { |
| 316 | + $params[$field][ApiBase::PARAM_ISMULTI] = true; |
| 317 | + } |
| 318 | + |
| 319 | + if ( $setDefaults && $hasDefault ) { |
| 320 | + $default = is_array( $defaults[$field] ) ? implode( '|', $defaults[$field] ) : $defaults[$field]; |
| 321 | + $params[$field][ApiBase::PARAM_DFLT] = $default; |
| 322 | + } |
| 323 | + } |
| 324 | + |
| 325 | + return $params; |
| 326 | + } |
| 327 | + |
| 328 | + /** |
| 329 | + * Returns an array with the fields and their descriptions. |
| 330 | + * |
| 331 | + * field name => field description |
| 332 | + * |
| 333 | + * @since 1.20 |
| 334 | + * |
| 335 | + * @return array |
| 336 | + */ |
| 337 | + public function getFieldDescriptions() { |
| 338 | + return array(); |
| 339 | + } |
| 340 | + |
| 341 | + /** |
| 342 | + * Get the database type used for read operations. |
| 343 | + * |
| 344 | + * @since 1.20 |
| 345 | + * |
| 346 | + * @return integer DB_ enum |
| 347 | + */ |
| 348 | + public function getReadDb() { |
| 349 | + return $this->readDb; |
| 350 | + } |
| 351 | + |
| 352 | + /** |
| 353 | + * Set the database type to use for read operations. |
| 354 | + * |
| 355 | + * @param integer $db |
| 356 | + * |
| 357 | + * @since 1.20 |
| 358 | + */ |
| 359 | + public function setReadDb( $db ) { |
| 360 | + $this->readDb = $db; |
| 361 | + } |
| 362 | + |
| 363 | + /** |
| 364 | + * Update the records matching the provided conditions by |
| 365 | + * setting the fields that are keys in the $values param to |
| 366 | + * their corresponding values. |
| 367 | + * |
| 368 | + * @since 1.20 |
| 369 | + * |
| 370 | + * @param array $values |
| 371 | + * @param array $conditions |
| 372 | + * |
| 373 | + * @return boolean Success indicator |
| 374 | + */ |
| 375 | + public function update( array $values, array $conditions = array() ) { |
| 376 | + $dbw = wfGetDB( DB_MASTER ); |
| 377 | + |
| 378 | + return $dbw->update( |
| 379 | + $this->getDBTable(), |
| 380 | + $this->getPrefixedValues( $values ), |
| 381 | + $this->getPrefixedValues( $conditions ), |
| 382 | + __METHOD__ |
| 383 | + ); |
| 384 | + } |
| 385 | + |
| 386 | + /** |
| 387 | + * Computes the values of the summary fields of the objects matching the provided conditions. |
| 388 | + * |
| 389 | + * @since 1.20 |
| 390 | + * |
| 391 | + * @param array|string|null $summaryFields |
| 392 | + * @param array $conditions |
| 393 | + */ |
| 394 | + public function updateSummaryFields( $summaryFields = null, array $conditions = array() ) { |
| 395 | + $this->setReadDb( DB_MASTER ); |
| 396 | + |
| 397 | + foreach ( $this->select( null, $conditions ) as /* DBDataObject */ $item ) { |
| 398 | + $item->loadSummaryFields( $summaryFields ); |
| 399 | + $item->setSummaryMode( true ); |
| 400 | + $item->saveExisting(); |
| 401 | + } |
| 402 | + |
| 403 | + $this->setReadDb( DB_SLAVE ); |
| 404 | + } |
| 405 | + |
| 406 | + /** |
| 407 | + * Takes in an associative array with field names as keys and |
| 408 | + * their values as value. The field names are prefixed with the |
| 409 | + * db field prefix. |
| 410 | + * |
| 411 | + * Field names can also be provided as an array with as first element a table name, such as |
| 412 | + * $conditions = array( |
| 413 | + * array( array( 'tablename', 'fieldname' ), $value ), |
| 414 | + * ); |
| 415 | + * |
| 416 | + * @since 1.20 |
| 417 | + * |
| 418 | + * @param array $values |
| 419 | + * |
| 420 | + * @return array |
| 421 | + */ |
| 422 | + public function getPrefixedValues( array $values ) { |
| 423 | + $prefixedValues = array(); |
| 424 | + |
| 425 | + foreach ( $values as $field => $value ) { |
| 426 | + if ( is_integer( $field ) ) { |
| 427 | + if ( is_array( $value ) ) { |
| 428 | + $field = $value[0]; |
| 429 | + $value = $value[1]; |
| 430 | + } |
| 431 | + else { |
| 432 | + $value = explode( ' ', $value, 2 ); |
| 433 | + $value[0] = $this->getPrefixedField( $value[0] ); |
| 434 | + $prefixedValues[] = implode( ' ', $value ); |
| 435 | + continue; |
| 436 | + } |
| 437 | + } |
| 438 | + |
| 439 | + $prefixedValues[$this->getPrefixedField( $field )] = $value; |
| 440 | + } |
| 441 | + |
| 442 | + return $prefixedValues; |
| 443 | + } |
| 444 | + |
| 445 | + /** |
| 446 | + * Takes in a field or array of fields and returns an |
| 447 | + * array with their prefixed versions, ready for db usage. |
| 448 | + * |
| 449 | + * @since 1.20 |
| 450 | + * |
| 451 | + * @param array|string $fields |
| 452 | + * |
| 453 | + * @return array |
| 454 | + */ |
| 455 | + public function getPrefixedFields( array $fields ) { |
| 456 | + foreach ( $fields as &$field ) { |
| 457 | + $field = $this->getPrefixedField( $field ); |
| 458 | + } |
| 459 | + |
| 460 | + return $fields; |
| 461 | + } |
| 462 | + |
| 463 | + /** |
| 464 | + * Takes in a field and returns an it's prefixed version, ready for db usage. |
| 465 | + * |
| 466 | + * @since 1.20 |
| 467 | + * |
| 468 | + * @param string|array $field |
| 469 | + * |
| 470 | + * @return string |
| 471 | + */ |
| 472 | + public function getPrefixedField( $field ) { |
| 473 | + return $this->getFieldPrefix() . $field; |
| 474 | + } |
| 475 | + |
| 476 | + /** |
| 477 | + * Takes an array of field names with prefix and returns the unprefixed equivalent. |
| 478 | + * |
| 479 | + * @since 1.20 |
| 480 | + * |
| 481 | + * @param array $fieldNames |
| 482 | + * |
| 483 | + * @return array |
| 484 | + */ |
| 485 | + public function unprefixFieldNames( array $fieldNames ) { |
| 486 | + return array_map( '$this->unprefixFieldName', $fieldNames ); |
| 487 | + } |
| 488 | + |
| 489 | + /** |
| 490 | + * Takes a field name with prefix and returns the unprefixed equivalent. |
| 491 | + * |
| 492 | + * @since 1.20 |
| 493 | + * |
| 494 | + * @param string $fieldName |
| 495 | + * |
| 496 | + * @return string |
| 497 | + */ |
| 498 | + public function unprefixFieldName( $fieldName ) { |
| 499 | + return substr( $fieldName, strlen( $this->getFieldPrefix() ) ); |
| 500 | + } |
| 501 | + |
| 502 | + public function __construct() { |
| 503 | + |
| 504 | + } |
| 505 | + |
| 506 | + /** |
| 507 | + * Get an instance of this class. |
| 508 | + * |
| 509 | + * @since 1.20 |
| 510 | + * |
| 511 | + * @return DBtable |
| 512 | + */ |
| 513 | + public static function &singleton() { |
| 514 | + static $instance; |
| 515 | + |
| 516 | + if ( !isset( $instance ) ) { |
| 517 | + $instance = new static; |
| 518 | + } |
| 519 | + |
| 520 | + return $instance; |
| 521 | + } |
| 522 | + |
| 523 | +} |
Property changes on: trunk/phase3/includes/DBTable.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 524 | + native |