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