Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.55% covered (warning)
89.55%
60 / 67
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
CampaignStore
89.55% covered (warning)
89.55%
60 / 67
77.78% covered (warning)
77.78%
7 / 9
18.37
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSelectFields
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 getSelectTables
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getJoinConds
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 newRecordFromRow
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
4
 newSelectQueryBuilder
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 upsertCampaign
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
3
 deleteCampaignByPageId
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getCampaignByDBKey
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace MediaWiki\Extension\MediaUploader\Campaign;
4
5use DBAccessObjectUtils;
6use FormatJson;
7use IDBAccessObject;
8use stdClass;
9use Title;
10use Wikimedia\Rdbms\ILoadBalancer;
11
12/**
13 * Provides access to the mu_campaign database table.
14 */
15class CampaignStore implements IDBAccessObject {
16
17    // Constants controlling how much information from the DB should be retrieved.
18    /** @var int Only the id, enabled and validity fields */
19    public const SELECT_MINIMAL = 0;
20    /** @var int The content of the campaign */
21    public const SELECT_CONTENT = 1;
22    /** @var int The title of the corresponding page */
23    public const SELECT_TITLE = 2;
24
25    /** @var ILoadBalancer */
26    private $loadBalancer;
27
28    /**
29     * @param ILoadBalancer $loadBalancer
30     *
31     * @internal only for use by ServiceWiring
32     */
33    public function __construct( ILoadBalancer $loadBalancer ) {
34        $this->loadBalancer = $loadBalancer;
35    }
36
37    /**
38     * Returns an array of all fields in the mu_campaign table.
39     *
40     * @param int $selectFlags Bitfield of self::SELECT_* constants
41     *
42     * @return string[]
43     */
44    public function getSelectFields(
45        int $selectFlags = self::SELECT_MINIMAL
46    ): array {
47        $fields = [
48            'campaign_page_id',
49            'campaign_enabled',
50            'campaign_validity',
51        ];
52        if ( self::SELECT_CONTENT & $selectFlags ) {
53            $fields[] = 'campaign_content';
54        }
55        if ( self::SELECT_TITLE & $selectFlags ) {
56            $fields[] = 'page_title';
57            $fields[] = 'page_namespace';
58        }
59
60        return $fields;
61    }
62
63    /**
64     * Returns an array of tables for SELECT queries.
65     *
66     * @param int $selectFlags Bitfield of self::SELECT_* constants
67     *
68     * @return string[]
69     */
70    public function getSelectTables(
71        int $selectFlags = self::SELECT_MINIMAL
72    ): array {
73        $tables = [ 'mu_campaign' ];
74        if ( self::SELECT_TITLE & $selectFlags ) {
75            $tables[] = 'page';
76        }
77
78        return $tables;
79    }
80
81    /**
82     * Returns an array of join conditions for SELECT queries.
83     *
84     * @param int $selectFlags Bitfield of self::SELECT_* constants
85     *
86     * @return array
87     */
88    public function getJoinConds(
89        int $selectFlags = self::SELECT_MINIMAL
90    ): array {
91        if ( self::SELECT_TITLE & $selectFlags ) {
92            return [ 'page' => [ 'JOIN', 'campaign_page_id = page_id' ] ];
93        }
94        return [];
95    }
96
97    /**
98     * Constructs a new CampaignRecord from a DB row.
99     *
100     * @param stdClass $row
101     * @param int $selectFlags Bitfield of self::SELECT_* constants used to
102     *   retrieve the row from the DB.
103     *
104     * @return CampaignRecord
105     */
106    public function newRecordFromRow(
107        stdClass $row,
108        int $selectFlags
109    ): CampaignRecord {
110        $content = null;
111        if ( self::SELECT_CONTENT & $selectFlags && $row->campaign_content ) {
112            $content = FormatJson::parse(
113                $row->campaign_content,
114                FormatJson::FORCE_ASSOC
115            )->getValue();
116        }
117
118        $title = null;
119        if ( self::SELECT_TITLE & $selectFlags ) {
120            // Performance hack: in case any code down the line decides to obtain
121            // the page ID from the Title object, we set that info artificially on
122            // the DB row, as we know it from the mu_campaign table.
123            $row->page_id = $row->campaign_page_id;
124            $title = Title::newFromRow( $row );
125        }
126
127        return new CampaignRecord(
128            $row->campaign_page_id,
129            (bool)$row->campaign_enabled,
130            $row->campaign_validity,
131            $content,
132            $title
133        );
134    }
135
136    /**
137     * Constructs a new CampaignSelectQueryBuilder.
138     *
139     * @param int $queryFlags bitfield of self::READ_* constants
140     *
141     * @return CampaignSelectQueryBuilder
142     */
143    public function newSelectQueryBuilder(
144        int $queryFlags = self::READ_NORMAL
145    ): CampaignSelectQueryBuilder {
146        [ $mode, $options ] = DBAccessObjectUtils::getDBOptions( $queryFlags );
147        $db = $this->loadBalancer->getConnectionRef( $mode );
148        $queryBuilder = new CampaignSelectQueryBuilder( $db, $this );
149        $queryBuilder->options( $options );
150
151        return $queryBuilder;
152    }
153
154    /**
155     * Insert or update an existing row in the database.
156     *
157     * @param CampaignRecord $record
158     */
159    public function upsertCampaign( CampaignRecord $record ): void {
160        $db = $this->loadBalancer->getConnectionRef( DB_PRIMARY );
161        $content = $record->getContent();
162        if ( $content !== null ) {
163            $content = FormatJson::encode( $content );
164        }
165
166        $set = [
167            'campaign_enabled' => $record->isEnabled() ? 1 : 0,
168            'campaign_validity' => $record->getValidity(),
169            'campaign_content' => $content,
170        ];
171        $db->upsert(
172            'mu_campaign',
173            $set + [ 'campaign_page_id' => $record->getPageId() ],
174            'campaign_page_id',
175            $set,
176            __METHOD__
177        );
178    }
179
180    /**
181     * Delete a campaign row in the DB by its page id (PK).
182     *
183     * @param int $pageId
184     */
185    public function deleteCampaignByPageId( int $pageId ): void {
186        $db = $this->loadBalancer->getConnectionRef( DB_PRIMARY );
187        $db->delete(
188            'mu_campaign',
189            [ 'campaign_page_id' => $pageId ],
190            __METHOD__
191        );
192    }
193
194    /**
195     * Convenience function to retrieve a record for a given DB key.
196     *
197     * @param string $dbKey
198     * @param int $selectFlags Bitfield of self::SELECT_* constants used to
199     *   retrieve the row from the DB. SELECT_TITLE is always included
200     *   regardless of this parameter.
201     * @param int $queryFlags bitfield of self::READ_* constants
202     *
203     * @return CampaignRecord|null
204     */
205    public function getCampaignByDBKey(
206        string $dbKey,
207        int $selectFlags = self::SELECT_TITLE,
208        int $queryFlags = self::READ_NORMAL
209    ): ?CampaignRecord {
210        $selectFlags |= self::SELECT_TITLE;
211        return $this->newSelectQueryBuilder( $queryFlags )
212            ->where( [ 'page_title' => $dbKey ] )
213            ->fetchCampaignRecord( $selectFlags );
214    }
215}