1 <?php
2 /* --------------------------------------------------------------
3 EmailReader.inc.php 2015-01-29 gm
4 Gambio GmbH
5 http://www.gambio.de
6 Copyright (c) 2015 Gambio GmbH
7 Released under the GNU General Public License (Version 2)
8 [http://www.gnu.org/licenses/gpl-2.0.html]
9 --------------------------------------------------------------
10 */
11
12 MainFactory::load_class('EmailReaderInterface');
13
14 /**
15 * Class EmailReader
16 *
17 * Reads email records from the database. This class provides a customizable interface
18 * for reading operations so that it is possible to build different variations in the
19 * EmailRepository class (e.g. "getPending", "findById", "getAll").
20 *
21 * @category System
22 * @package Email
23 * @subpackage Repository
24 */
25 class EmailReader implements EmailReaderInterface
26 {
27 /**
28 * Query builder.
29 * @var CI_DB_query_builder
30 */
31 protected $db;
32
33 /**
34 * E-Mail factory.
35 * @var EmailFactory
36 */
37 protected $factory;
38
39
40 /**
41 * Class Constructor
42 *
43 * @param CI_DB_query_builder $db Will be used for database operations.
44 * @param EmailFactoryInterface $factory Will be used for the creation of returned objects.
45 */
46 public function __construct(CI_DB_query_builder $db, EmailFactoryInterface $factory)
47 {
48 $this->db = $db;
49 $this->factory = $factory;
50 }
51
52
53 /**
54 * Get email records filtered by conditions.
55 *
56 * Example:
57 * $reader->get(array('email_id' => $customerId), 10, array( array('email_id', 'asc') ));
58 *
59 * @param array $conditions (optional) Contains conditions with column => value pairs.
60 * @param array $limit (optional) Array that contains LIMIT and OFFSET value
61 * e.g. array( 'limit' => 10, 'offset' => 5 )
62 * @param array $order (optional) Contains arrays with column, direction pairs
63 * e.g. array( 'column' => 'direction' )
64 *
65 * @return EmailCollection Returns a collection containing the email records.
66 */
67 public function get(array $conditions = array(), array $limit = array(), array $order = array())
68 {
69 $this->_limit($limit);
70 $this->_order($order);
71
72 $results = $this->db->get_where('emails', $conditions)->result_array();
73 $collection = MainFactory::create('EmailCollection');
74
75 foreach($results as &$item)
76 {
77 $item['contacts'] = $this->db->get_where('email_contacts', array('email_id' => $item['email_id']))
78 ->result_array();
79 $item['attachments'] = $this->db->get_where('email_attachments', array('email_id' => $item['email_id']))
80 ->result_array();
81 $collection->add($this->_createEmailByArray($item));
82 }
83
84 return $collection;
85 }
86
87
88 /**
89 * Filter email records with provided keyword string.
90 *
91 * @param string $p_keyword String to be used for filtering the email records.
92 * @param array $limit (optional) Array that contains LIMIT and OFFSET value
93 * e.g. array( 'limit' => 10, 'offset' => 5 )
94 * @param array $order (optional) Contains arrays with column, direction pairs
95 * e.g. array( 'column' => 'direction' )
96 *
97 * @return EmailCollection Returns a collection containing the email records.
98 */
99 public function filter($p_keyword, array $limit = array(), array $order = array())
100 {
101 $this->_filter($p_keyword);
102 $this->_limit($limit);
103 $this->_order($order);
104
105 $results = $this->db->get()->result_array();
106 $collection = MainFactory::create('EmailCollection');
107
108 foreach($results as &$item)
109 {
110 $item['contacts'] = $this->db->get_where('email_contacts', array('email_id' => $item['email_id']))
111 ->result_array();
112 $item['attachments'] = $this->db->get_where('email_attachments', array('email_id' => $item['email_id']))
113 ->result_array();
114 $collection->add($this->_createEmailByArray($item));
115 }
116
117 return $collection;
118 }
119
120
121 /**
122 * Get the current count of the email records in the database.
123 *
124 * This method will quickly return the record count of the "emails" table. It must
125 * be used when we just need the number and not the data, because the "get" or "find"
126 * methods need more time to load and parse the records.
127 *
128 * @param string $p_filterKeyword (optional) If provided the records will be filtered.
129 *
130 * @throws InvalidArgumentException If the provided argument is not a string.
131 *
132 * @return int Returns the row number of the email table.
133 */
134 public function getRecordCount($p_filterKeyword = '')
135 {
136 if(!is_string($p_filterKeyword))
137 {
138 throw new InvalidArgumentException('Invalid argument provided (string expected): '
139 . gettype($p_filterKeyword));
140 }
141
142 if(!empty($p_filterKeyword))
143 {
144 $this->_filter($p_filterKeyword);
145 $count = $this->db->count_all_results();
146 }
147 else
148 {
149 $count = $this->db->count_all('emails');
150 }
151
152 return (int)$count;
153 }
154
155
156 /**
157 * Creates an email object out of an array.
158 *
159 * This method expects the following values to be present in the array: 'email_id', 'subject',
160 * 'content', 'is_pending', 'contacts', 'attachments'. It uses the EmailFactory for creating
161 * email objects.
162 *
163 * @param array $emailDataArray Contains the database record information.
164 *
165 * @throws UnexpectedValueException If the 'creation_date' of the email is empty.
166 *
167 * @return Email Returns an object that represents the database record.
168 */
169 protected function _createEmailByArray(array $emailDataArray)
170 {
171 // Required email fields must always have a value.
172 $emailId = new IdType((int)$emailDataArray['email_id']);
173 $subject = (!empty($emailDataArray['subject'])) ? MainFactory::create('EmailSubject',
174 $emailDataArray['subject']) : null;
175 $isPending = (bool)$emailDataArray['is_pending'];
176
177 // Optional email fields might be empty. In that case we simply set a NULL value.
178 $contentHtml = (!empty($emailDataArray['content_html'])) ? MainFactory::create('EmailContent',
179 html_entity_decode_wrapper($emailDataArray['content_html'])) : null;
180 $contentPlain = (!empty($emailDataArray['content_plain'])) ? MainFactory::create('EmailContent',
181 $emailDataArray['content_plain']) : null;
182
183 $contacts = MainFactory::create('ContactCollection');
184 foreach($emailDataArray['contacts'] as $contactDataArray)
185 {
186 // Required Fields
187 $emailAddress = MainFactory::create('EmailAddress', $contactDataArray['email_address']);
188 $contactType = MainFactory::create('ContactType', $contactDataArray['contact_type']);
189
190 // Optional Field
191 $contactName = (!empty($contactDataArray['contact_name'])) ? MainFactory::create('ContactName',
192 $contactDataArray['contact_name']) : null;
193
194 $contacts->add($this->factory->createContact($emailAddress, $contactType, $contactName));
195 }
196
197 $attachments = MainFactory::create('AttachmentCollection');
198 foreach($emailDataArray['attachments'] as $attachmentDataArray)
199 {
200 // Required Field
201 $path = MainFactory::create('AttachmentPath', $attachmentDataArray['path']);
202
203 // Optional Field
204 $name = (!empty($attachmentDataArray['name'])) ? MainFactory::create('AttachmentName',
205 $attachmentDataArray['name']) : null;
206
207 $attachments->add(MainFactory::create('EmailAttachment', $path, $name));
208 }
209
210 $email = $this->factory->createEmail($emailId, $subject, $contentHtml, $contentPlain, $isPending, $contacts,
211 $attachments);
212
213 // All registered emails must have a creation date. If there is a record with no
214 // creation date then something wrong happened during the "send" or "queue" operations.
215 if($emailDataArray['creation_date'] === null)
216 {
217 throw new UnexpectedValueException('Email "creation_date" field must not be null.');
218 }
219
220 $email->setCreationDate(MainFactory::create('DateTime', $emailDataArray['creation_date']));
221
222 if($emailDataArray['sent_date'] !== null)
223 {
224 $email->setSentDate(MainFactory::create('DateTime', $emailDataArray['sent_date']));
225 }
226
227 return $email;
228 }
229
230
231 /**
232 * Apply filter rules to email records.
233 *
234 * This method will set the SQL filters depending the provided keyword so that one
235 * can "get" the filtered records.
236 *
237 * @param string $p_keyword Filtering keyword to be applied in the query.
238 *
239 * @throws InvalidArgumentException If the $keyword argument is not a string.
240 */
241 protected function _filter($p_keyword)
242 {
243 if(!is_string($p_keyword))
244 {
245 throw new InvalidArgumentException('Invalid argument provided (string expected) $keyword: ' . $p_keyword);
246 }
247
248 $this->db->select('emails.*')
249 ->distinct()
250 ->from('emails')
251 ->join('email_contacts', 'email_contacts.email_id = emails.email_id', 'left')
252 ->join('email_attachments', 'email_attachments.email_id = emails.email_id', 'left')
253 ->like('emails.subject', $p_keyword)
254 ->or_like('emails.content_html', $p_keyword)
255 ->or_like('emails.content_plain', $p_keyword)
256 ->or_like('emails.creation_date', $p_keyword)
257 ->or_like('emails.sent_date', $p_keyword)
258 ->or_like('email_contacts.email_address', $p_keyword)
259 ->or_like('email_contacts.contact_name', $p_keyword)
260 ->or_like('email_attachments.path', $p_keyword)
261 ->or_like('email_attachments.name', $p_keyword);
262 }
263
264
265 /**
266 * Apply LIMIT clause to query.
267 *
268 * Example: $this->_limit( array( 'limit' => 10, 'offset' => 0 ) );
269 *
270 * @link http://www.codeigniter.com/userguide3/database/query_builder.html#limiting-or-counting-results
271 *
272 * @param array $rule Must be an array that contains 'limit' and 'offset' values.
273 */
274 protected function _limit(array $rule)
275 {
276 if(!empty($rule))
277 {
278 $this->db->limit((int)$rule['limit'], (int)$rule['offset']);
279 }
280 }
281
282
283 /**
284 * Apply ORDER BY clause to query.
285 *
286 * Example: $this->_order( array( 'email_id' => 'desc' ) );
287 *
288 * @link http://www.codeigniter.com/userguide3/database/query_builder.html#ordering-results
289 *
290 * @param array $rule Contains column, direction arrays for ordering results.
291 */
292 protected function _order(array $rule)
293 {
294 foreach($rule as $column => $direction)
295 {
296 $this->db->order_by($column, $direction);
297 }
298 }
299 }