Blogger   D
last analyzed

Complexity

Total Complexity 59

Size/Duplication

Total Lines 484
Duplicated Lines 0 %

Importance

Changes 31
Bugs 0 Features 2
Metric Value
eloc 141
c 31
b 0
f 2
dl 0
loc 484
rs 4.08
wmc 59

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 3
A setName() 0 3 2
A setBlog() 0 3 2
B install() 0 42 6
A getName() 0 2 1
A getHits() 0 2 1
A loadScripts() 0 3 1
A getBlogManager() 0 2 1
A getPost() 0 2 1
A getRecentPosts() 0 2 1
A deletePost() 0 2 1
A getPosts() 0 2 1
B savePost() 0 15 7
A fontsAwesome() 0 2 1
A handlePublishPost() 0 6 4
A w3css() 0 2 1
A renderPost() 0 10 4
A getPostsCount() 0 2 1
A loadEditor() 0 16 3
B renderPostItems() 0 19 9
A searchPosts() 0 2 1
A handleSavePost() 0 8 4
A publishPost() 0 2 1
A metaOg() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like Blogger often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Blogger, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types=1);
3
defined('BASEPATH') OR exit('No direct script access allowed');
4
5
class Blogger {
6
7
  /**
8
   * Code Igniter Instance.
9
   * @var object
10
   */
11
  private $ci;
12
  /**
13
   * Code Igniter DB Forge instance reference for simplicity.
14
   * @var object
15
   */
16
  private $dbforge;
17
  /**
18
   * Current Blog Table Name.
19
   * @var string
20
   */
21
  private $table_name;
22
  /**
23
   * String prefixed with ever blog name.
24
   * @var string
25
   */
26
  const TABLE_PREFIX = "blogger_posts";
27
  /**
28
   * Name of this package for simplicity.
29
   * @var string
30
   */
31
  const PACKAGE = "francis94c/blog";
32
  /**
33
   * Name of the dependent package for markdown.
34
   * @var string
35
   */
36
  const MARKDOWN_PACKAGE = "francis94c/ci-parsedown";
37
  /**
38
   * Blog post create action.
39
   * @var string
40
   */
41
  const CREATE = "create";
42
  /**
43
   * Blog post create and publish action.
44
   * @var string
45
   */
46
  const CREATE_AND_PUBLISH = "createAndPublish";
47
  /**
48
   * Blog post edit action.
49
   * @var string
50
   */
51
  const EDIT = "edit";
52
  /**
53
   * Blog post publish action.
54
   * @var string
55
   */
56
  const PUBLISH = "publish";
57
  /**
58
   * Blog post delete action.
59
   * @var string
60
   */
61
  const DELETE = "delete";
62
  /**
63
   * Blog post abort acction. This is an action taken internally when other
64
   * actions fail.
65
   * @var string
66
   */
67
  const ABORT = "abortAction";
68
  /**
69
   * Blog post no action.
70
   * @var string
71
   */
72
  const NO_ACTION = "no_action";
73
  /**
74
   * Constructor
75
   * @param mixed $params associative array of parameters. See README.md
76
   */
77
  function __construct($params=null) {
78
    $this->ci =& /** @scrutinizer ignore-call */ get_instance();
79
    $this->ci->load->database();
80
    $this->table_name = self::TABLE_PREFIX . (isset($params["name"]) ? "_" . $params["name"] : "");
81
    $this->ci->load->database();
82
    $this->ci->load->splint(self::PACKAGE, "*BlogManager", "bmanager");
83
    $this->ci->load->splint(self::MARKDOWN_PACKAGE, "+Parsedown", null, "parsedown");
84
    $this->ci->bmanager->setBlogName(isset($params["name"]) ? $params["name"] : null);
85
    $this->ci->load->helper("url");
86
  }
87
  /**
88
   * Installs a blog with the given table name and paramters.
89
   *
90
   * @param  string $blogName                Name of blog tabke to install.
91
   *
92
   * @param  string $adminTableName          Name of admi table to restrict post to.
93
   *
94
   * @param  string $adminIdColumnName       Name of the column to add a foreign
95
   *                                         key constarint to the blog table with.
96
   *
97
   * @param  int    $adminIdColumnConstraint The column constarint or limit of
98
   *                                         $adminIdColumnName.
99
   *
100
   * @return bool                            True on Success, False if Not.
101
   */
102
  public function install(string $blogName=null, string $adminTableName=null, string $adminIdColumnName=null, int $adminIdColumnConstraint=null): bool {
103
    $blogName = $blogName === null ? $this->table_name : self::TABLE_PREFIX . "_" . $blogName;
104
    $this->ci->load->dbforge();
105
    $this->ci->dbforge->add_field("id");
106
    $fields = array(
107
      "title" => array(
108
        "type"       => "VARCHAR",
109
        "constraint" => 70,
110
      ),
111
      "content" => array(
112
        "type" => "TEXT"
113
      ),
114
      "date_published" => array(
115
        "type" => "TIMESTAMP",
116
        "null" => true
117
      ),
118
      "published" => array(
119
        "type" => "TINYINT",
120
        "default" => 0
121
      ),
122
      "hits"      => array(
123
        "type"       => "INT",
124
        "constraint" => 7,
125
        "default"    => 0
126
      ),
127
      "slug"      => array(
128
        "type"       => "VARCHAR",
129
        "constraint" => 80,
130
        "unique"     => true
131
      )
132
    );
133
    $this->ci->dbforge->add_field($fields);
134
    $constrain = $adminTableName !== null && $adminIdColumnName !== null &&
135
    $adminIdColumnConstraint !== null;
136
    if ($constrain) {
137
      $this->ci->dbforge->add_field(
138
        "poster_id INT($adminIdColumnConstraint), FOREIGN KEY (poster_id) REFERENCES $adminTableName($adminIdColumnName)");
139
    }
140
    $this->ci->dbforge->add_field("date_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP");
141
    $attributes = array('ENGINE' => 'InnoDB');
142
    if (!$this->ci->dbforge->create_table($blogName, true, $attributes)) return false;
143
    return true;
144
  }
145
  /**
146
   * Sets the name of the current blog table.
147
   *
148
   * @param string $name name of a blog table.
149
   *
150
   * @deprecated
151
   */
152
  public function setName(string $name): void {
153
    $this->table_name = self::TABLE_PREFIX . "_" . $name;
154
    $this->ci->bmanager->setBlogName($name != "" ? $name : null);
155
  }
156
  /**
157
   * Same as the deprecated setName. Sets the name of the current blog table.
158
   *
159
   * @param string $blog [description]
160
   */
161
  public function setBlog(string $blog): void {
162
    $this->table_name = self::TABLE_PREFIX . "_" . $blog;
163
    $this->ci->bmanager->setBlogName($blog != "" ? $blog : null);
164
  }
165
  /**
166
   * Gets the name of the blog.
167
   * @return string The name of the blog.
168
   */
169
  public function getName(): string {
170
    return $this->table_name;
171
  }
172
  /**
173
   * [getBlogManager Returns the BlogManager Model object.]
174
   * 
175
   * @return BlogManager BlogManager Model Object.
176
   */
177
  public function &getBlogManager(): BlogManager {
178
    return $this->ci->bmanager;
179
  }
180
  /**
181
   * Loads/echoes the client side scripts needed for the blog to render it's
182
   * post editor and other views.
183
   *
184
   * @param  bool $w3css If true, additionally loads the W3.CSS file for additional
185
   *                     styling. Defaults internally on Blogmanager to true
186
   */
187
  private function loadScripts(bool $w3css): void {
188
    $this->ci->load->splint(self::PACKAGE, "-header_scripts", array(
189
      "w3css" => $w3css
190
    ));
191
  }
192
  /**
193
   * Returns the W3.CSS client side script loading tag.
194
   * @return string W3.CSS link tag.
195
   */
196
  public function w3css(): string {
197
    return "<link rel=\"stylesheet\" href=\"https://www.w3schools.com/w3css/4/w3.css\">";
198
  }
199
  /**
200
   * Returns the Fonts Awesome CSS link loading tag.
201
   * @return string Fonts Awesome CSS link loading tag.
202
   */
203
  public function fontsAwesome(): string {
204
    return "<link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.3.1/css/all.css\"/>";
205
  }
206
  /**
207
   * Echoes to the browser a 'SimpleMDE' markdown editor for editing post
208
   * contents, as part of a form.
209
   *
210
   * @param  string  $callback The URI callback that will be passed to the Code
211
   *                           Igniter form_open method when outputing the form.
212
   *                           The call back is where you should read the contents
213
   *                           of the submited form.
214
   *                           The contents of the form should be read or handled
215
   *                           by a call to the 'savePost($posterId)' function.
216
   *                           You don't need to worry about reading it your self.
217
   *
218
   * @param  int     $postId   (Optional) The ID of the post whose content should
219
   *                           be present in the editor when loaded. provide this
220
   *                           parameter when you want to edit a post.
221
   *
222
   * @param  bool    $w3css    If true, echoes the W3.CSS link tag as well.
223
   *
224
   * @return bool              True  if sucessfull without errors, false if not.
225
   */
226
  public function loadEditor(string $callback, int $postId=null, bool $w3css=true): bool {
227
    $this->loadScripts($w3css);
228
    $this->ci->load->helper("form");
229
    $data = array(
230
      "callback" => "Admin/token",
231
      "type"     => $postId === null ? "create" : "edit",
232
      "callback" => $callback
233
    );
234
    if ($postId !== null) {
235
      $data["id"] = $postId;
236
      $post = $this->getPost($postId, false);
237
      $data["title"] = $post["title"];
238
      $data["content"] = $post["content"];
239
    }
240
    $this->ci->load->splint("francis94c/blog", "-post_edit", $data);
241
    return true;
242
  }
243
  /**
244
   * Handles form data from the Editor loaded by a call to 'loadEditor'.
245
   * Traditionally, this \function is to be called ath the controller function
246
   * specified by the callback URI provided to the loadEditor method.
247
   *
248
   * @param  int $posterId ID of the poster. A valid admin ID from the table
249
   *                       specified as a foreign key constraint during the
250
   *                       installation of the selected blog.
251
   *
252
   * @return string        The final action reached in processing the form
253
   *                       inputs. These are public string constants declared in
254
   *                       this file.
255
   */
256
  public function savePost(int $posterId=null): string {
257
    $action = $this->ci->security->xss_clean($this->ci->input->post("action"));
258
    switch ($action) {
259
      case "save":
260
        return $this->handleSavePost($posterId);
261
      case "publish":
262
        return $this->handlePublishPost($posterId);
263
      case "createAndPublish":
264
        if ($this->ci->bmanager->createAndPublishPost($this->ci->security->xss_clean($this->ci->input->post("title")), $this->ci->security->xss_clean($this->ci->input->post("editor")), $posterId) !== false) return self::CREATE_AND_PUBLISH;
265
        return self::ABORT;
266
      case "delete":
267
        if ($this->ci->bmanager->deletePost($this->ci->security->xss_clean($this->ci->input->post("id")))) return self::DELETE;
268
        return self::ABORT;
269
      default:
270
        return self::NO_ACTION;
271
    }
272
  }
273
  /**
274
   * [handleSavePost handles save pot actions; edit & create]
275
   *
276
   * @param  int     $posterId  Poster ID or Admin ID.
277
   *
278
   * @return string             Action taken during the pocess; Blogger::CREATE Or Blogger::EDIT
279
   */
280
  private function handleSavePost(int $posterId=null): string {
281
    $id = $this->ci->security->xss_clean($this->ci->input->post("id"));
282
    if ($id != "") {
283
      if (!$this->ci->bmanager->savePost($this->ci->input->post("id"), $this->ci->security->xss_clean($this->ci->input->post("title")), $this->ci->security->xss_clean($this->ci->input->post("editor")), $posterId)) return self::ABORT;
284
      return self::EDIT;
285
    } else {
286
      if ($this->ci->bmanager->createPost($this->ci->security->xss_clean($this->ci->input->post("title")), $this->ci->security->xss_clean($this->ci->input->post("editor")), $posterId) == 0) return self::ABORT;
287
      return self::CREATE;
288
    }
289
  }
290
  /**
291
   * [handlePublishPost handles the publishing of a post using inputs from the
292
   *  submited form.]
293
   *
294
   * @param  int    $posterId ID of the publishing Admin.
295
296
   * @return string           Action reached while processing form inputs.
297
   */
298
  private function handlePublishPost(int $posterId=null): string {
299
    $id = $this->ci->security->xss_clean($this->ci->input->post("id"));
300
    if ($id == "") return self::ABORT;
301
    if (!$this->ci->bmanager->savePost($id, $this->ci->security->xss_clean($this->ci->input->post("title")), $this->ci->security->xss_clean($this->ci->input->post("editor")), $posterId)) return self::ABORT;
302
    if (!$this->ci->bmanager->publishPost($id, true)) return self::ABORT;
303
    return self::PUBLISH;
304
  }
305
  /**
306
   * getPosts get posts from the database by the given $page starting from the
307
   * value of 1 and returns $limit number of rows.
308
   *
309
   * @param  int     $page   Page number starting from 1.
310
   *
311
   * @param  int     $limit  Number of posts to return.
312
   *
313
   * @param  bool    $filter if true, returns only published posts, if false
314
   *                         return all posts. false by default.
315
   *
316
   * @param  bool    $hits   If truem orders the returned posts by number of hits.
317
   *
318
   * @return array           Array of posts for a given page.
319
   */
320
  public function getPosts(int $page, int $limit, bool $filter=false, bool $hits=false): array {
321
    return $this->ci->bmanager->getPosts($page, $limit, $filter, $hits);
322
  }
323
  /**
324
   * [renderPosts render post items by page number, max page count, and specified
325
   * view files.]
326
   * @param  string  $view       Custom view per post summary to use, from
327
   *                             end-user application context.
328
   *
329
   * @param  string  $callback   Callback URI that should render a full post for
330
   *                             each of the post items. This callback URI will
331
   *                             have the ID of each respective post, appended to
332
   *                             the end of it for each of the post itesm.
333
   *
334
   * @param  string  $empty_view View to load if blog has no posts, form end-user context.
335
   *
336
   * @param  int     $page       Page Number of pot list.
337
   *
338
   * @param  int     $limit      Max post summary per page.
339
   *
340
   * @param  boolean $filter     If true, ommits un-published posts.
341
   *
342
   * @param  boolean $hits       If true, orders returned posts by hits.
343
   *
344
   * @return bool                True if successful, false if not.
345
   */
346
  public function renderPostItems(string $view=null, string $callback=null, string $empty_view=null, int $page=1, int $limit=5, bool $filter=false, bool $hits=false, bool $slug=true): bool {
347
    if ($view === null || $empty_view === null) $this->ci->load->bind("francis94c/blog", $blogger);
348
    $posts = $this->getPosts($page, $limit, $filter, $hits);
349
    if (count($posts) == 0) {
350
      if ($empty_view === null) { $blogger->load->view("empty"); } else {
351
        $this->ci->load->view($empty_view);
352
        return true;
353
      }
354
    }
355
    $this->ci->load->helper("text");
356
    foreach ($posts as $post) {
357
      $post["callback"] = $callback !== null ? trim($callback, "/") . "/" . ($slug ? $post["slug"] : $post["id"]) : "";
358
      $post["filter"] = $filter;
359
      $post["content"] = $this->ci->parsedown->text(ellipsize($post["content"], 300));
360
      if ($view === null) {$blogger->load->view("post_list_item", $post); } else {
361
        $this->ci->load->view($view, $post);
362
      }
363
    }
364
    return true;
365
  }
366
  /**
367
   * [getRecentPosts gets most recent post, limits number returned with $limit.]
368
   *
369
   * @param  int  $limit  Number of Posts.
370
   *
371
   * @param  bool $filter If true, returns only published posts.
372
   *
373
   * @return array        Array of posts ordered by ID (Most recent first).
374
   */
375
  public function getRecentPosts(int $limit=5, bool $filter=false): array {
376
    return $this->ci->bmanager->getRecentPosts($limit, $filter);
377
  }
378
  /**
379
   * [renderPost renders a single blog post. uses it's default view if not
380
   * provided a $view]
381
   *
382
   * @param  array|int|string $post A post array, ID or Slug.
383
   *
384
   * @param  string           $view An alternate view to use for the body of the
385
   *                                post.
386
   *
387
   * @return bool             True if successful, false if not.
388
   */
389
  public function renderPost($post, string $view=null): bool {
390
    if (!is_array($post)) $post = $this->ci->bmanager->getPost($post);
391
    if (!$post) return false;
392
    $post["content"] = $this->ci->parsedown->text($post["content"]);
393
    if ($view === null) {
394
      $this->ci->load->splint("francis94c/blog", "-post_item", $post);
395
    } else {
396
      $this->ci->load->view($view, $post);
397
    }
398
    return true;
399
  }
400
  /**
401
   * [metaOg A utility \function that generates Open Graph HTML tags from a post
402
   * array. This \function should be hit from the controller method that loads a
403
   * post for correct results as there's a call to the 'current_url' of Code
404
   * Igniter's url helper here.]
405
   *
406
   * @param  array   $post Post array.
407
   *
408
   * @return string        Open Graph HTML Meta Tags.
409
   */
410
  public function metaOg(array $post): string {
411
    $data = array();
412
    $data["title"] = $post["title"];
413
    $data["description"] = substr($post["content"], 0, 154);
414
    if (isset($post["share_image"])) $data["image_link"] = $post["share_image"];
415
    $data["url"] = current_url();
416
    // Return Meta OG View String.
417
    return $this->ci->load->splint(self::PACKAGE, "-meta_og", $data, true);
418
  }
419
  /**
420
   * [getPostsCount gets total number of posts in the current blog]
421
   *
422
   * TODO: Get for a given blog.
423
   *
424
   * @param  bool $filter If true, counts only published posts. True by default.
425
   *
426
   * @return int          Posts count.
427
   */
428
  public function getPostsCount(bool $filter=true): int {
429
    return $this->ci->bmanager->getPostsCount($filter);
430
  }
431
  /**
432
   * [getPost gets a post array.]
433
   *
434
   * @param  int    $postId ID of the post to retrieve.
435
   *
436
   * @param  bool   $hit    Increment the post's hit count.
437
   *
438
   * @return array          An associative array of a single post.
439
   */
440
  public function getPost($postId, $hit=true) {
441
    return $this->ci->bmanager->getPost($postId, $hit);
442
  }
443
  /**
444
   * [getHits gets the number of hits for a particular post.]
445
   *
446
   * @param  int    $postId Post ID.
447
   *
448
   * @return int            Number of hits (Times viewed or read).
449
   */
450
  public function getHits(int $postId): int {
451
    return $this->ci->bmanager->getHits($postId);
452
  }
453
  /**
454
   * [publishPost Publish or Un Publish a Post.]
455
   * @param  int    $postId  Post ID
456
   * @param  bool   $publish Publish Or Un-Publish
457
   * @return bool            True if operation was successful, False if not.
458
   */
459
  public function publishPost(int $postId, bool $publish): bool {
460
    return $this->ci->bmanager->publishPost($postId, $publish);
461
  }
462
  /**
463
   * [deletePost Deletes a Post.]
464
   *
465
   * @param  int    $postId Post ID
466
   *
467
   * @return bool           True if successful, false if not.
468
   */
469
  public function deletePost(int $postId): bool {
470
    return $this->ci->bmanager->deletePost($postId);
471
  }
472
  /**
473
   * [searchPosts Searches through posts in the blog/database.]
474
   *
475
   * @param  string  $words  Word/Phrase to search for in Title and Content
476
   *                         fields.
477
   *
478
   * @param  int     $page   Page Number to retrive from result. Default is 1
479
   *
480
   * @param  int     $limit  The max number peer page. Default is 5.
481
   *
482
   * @param  bool    $filter If true, returns only matched pulished posts, else
483
   *                         returns all matched posts.
484
   *
485
   * @return array           Array of matched posts.
486
   */
487
  public function searchPosts(string $words, int $page=1, int $limit=5, bool $filter=false): array {
488
    return $this->ci->bmanager->searchPosts($words, $page, $limit, $filter);
489
  }
490
}
491