Completed
Push — master ( c2cfef...1a5a57 )
by Angus
02:24
created

Tracker_Title_Model::updateFailedChecksByID()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 7
ccs 0
cts 5
cp 0
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php declare(strict_types=1); defined('BASEPATH') OR exit('No direct script access allowed');
2
3
class Tracker_Title_Model extends Tracker_Base_Model {
4 119
	public function __construct() {
5 119
		parent::__construct();
6 119
	}
7
8
	/**
9
	 * @param string $titleURL
10
	 * @param int    $siteID
11
	 * @param bool   $create
12
	 * @param bool   $returnData
13
	 *
14
	 * @return array|int
15
	 */
16
	public function getID(string $titleURL, int $siteID, bool $create = TRUE, bool $returnData = FALSE) {
17
		$query = $this->db->select('tracker_titles.id, tracker_titles.title, tracker_titles.title_url, tracker_titles.latest_chapter, tracker_titles.status, tracker_sites.site_class, (tracker_titles.last_checked > DATE_SUB(NOW(), INTERVAL 3 DAY)) AS active', FALSE)
18
		                  ->from('tracker_titles')
19
		                  ->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
20
		                  ->where('tracker_titles.title_url', $titleURL)
21
		                  ->where('tracker_titles.site_id', $siteID)
22
		                  ->get();
23
24
		if($query->num_rows() > 0) {
25
			$id = (int) $query->row('id');
26
27
			//This updates inactive series if they are newly added, as noted in https://github.com/DakuTree/manga-tracker/issues/5#issuecomment-247480804
28
			if(((int) $query->row('active')) === 0 && $query->row('status') === 0) {
29
				$titleData = $this->sites->{$query->row('site_class')}->getTitleData($query->row('title_url'));
30
				if(!is_null($titleData['latest_chapter'])) {
31
					if($this->updateByID((int) $id, $titleData['latest_chapter'])) {
32
						//Make sure last_checked is always updated on successful run.
33
						//CHECK: Is there a reason we aren't just doing this in updateTitleById?
34
						$this->db->set('last_checked', 'CURRENT_TIMESTAMP', FALSE)
35
						         ->where('id', $id)
36
						         ->update('tracker_titles');
37
					}
38
				} else {
39
					log_message('error', "{$query->row('title')} failed to update successfully");
40
				}
41
			}
42
43
			$titleID = $id;
44
		} else {
45
			//TODO: Check if title is valid URL!
46
			if($create) $titleID = $this->addTitle($titleURL, $siteID);
47
		}
48
		if(!isset($titleID) || !$titleID) $titleID = 0;
49
50
		return ($returnData && $titleID !== 0 ? $query->row_array() : $titleID);
51
	}
52
53
	/**
54
	 * @param string $title
55
	 * @param string $siteURL
56
	 *
57
	 * @return array|int
58
	 * @throws Exception
59
	 */
60
	public function getIDFromData(string $title, string $siteURL) {
61
		if(!($siteData = $this->getSiteDataFromURL($siteURL))) {
62
			throw new Exception("Site URL is invalid: {$siteURL}");
63
		}
64
65
		return $this->getID($title, $siteData->id);
66
	}
67
68
	/**
69
	 * @param string $titleURL
70
	 * @param int    $siteID
71
	 *
72
	 * @return int
73
	 */
74
	private function addTitle(string $titleURL, int $siteID) : int {
75
		$query = $this->db->select('site, site_class')
76
		                  ->from('tracker_sites')
77
		                  ->where('id', $siteID)
78
		                  ->get();
79
80
		$titleData = $this->sites->{$query->row()->site_class}->getTitleData($titleURL, TRUE);
81
82
		//FIXME: getTitleData can fail, which will in turn cause the below to fail aswell, we should try and account for that
83
		if($titleData) {
84
			$this->db->insert('tracker_titles', array_merge($titleData, ['title_url' => $titleURL, 'site_id' => $siteID]));
85
			$titleID = $this->db->insert_id();
0 ignored issues
show
Bug introduced by
The method insert_id() does not exist on CI_DB_query_builder. Did you maybe mean insert()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
86
87
			$this->History->updateTitleHistory((int) $titleID, NULL, $titleData['latest_chapter'], $titleData['last_updated']);
88
		} else {
89
			log_message('error', "getTitleData failed for: {$query->row()->site_class} | {$titleURL}");
90
		}
91
		return $titleID ?? 0;
92
	}
93
94
95
	/**
96
	 * @param int    $titleID
97
	 * @param string $latestChapter
98
	 *
99
	 * @return bool
100
	 */
101
	public function updateByID(int $titleID, string $latestChapter) : bool {
102
		//FIXME: Really not too happy with how we're doing history stuff here, it just feels messy.
103
		$query = $this->db->select('latest_chapter AS current_chapter')
104
		                  ->from('tracker_titles')
105
		                  ->where('id', $titleID)
106
		                  ->get();
107
		$row = $query->row();
108
109
		$success = $this->db->set(['latest_chapter' => $latestChapter, 'failed_checks' => 0]) //last_updated gets updated via a trigger if something changes
110
		                    ->where('id', $titleID)
111
		                    ->update('tracker_titles');
112
113
		if($this->db->affected_rows() > 0) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class CI_DB_query_builder as the method affected_rows() does only exist in the following sub-classes of CI_DB_query_builder: CI_DB_cubrid_driver, CI_DB_ibase_driver, CI_DB_mssql_driver, CI_DB_mysql_driver, CI_DB_mysqli_driver, CI_DB_oci8_driver, CI_DB_pdo_4d_driver, CI_DB_pdo_cubrid_driver, CI_DB_pdo_dblib_driver, CI_DB_pdo_driver, CI_DB_pdo_firebird_driver, CI_DB_pdo_ibm_driver, CI_DB_pdo_informix_driver, CI_DB_pdo_mysql_driver, CI_DB_pdo_oci_driver, CI_DB_pdo_odbc_driver, CI_DB_pdo_pgsql_driver, CI_DB_pdo_sqlite_driver, CI_DB_pdo_sqlsrv_driver, CI_DB_postgre_driver, CI_DB_sqlite3_driver, CI_DB_sqlite_driver, CI_DB_sqlsrv_driver. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
114
			//Clear hidden latest chapter
115
			$this->db->set(['ignore_chapter' => 'NULL', 'last_updated' => 'last_updated'], NULL, FALSE)
116
			         ->where('title_id', $titleID)
117
			         ->update('tracker_chapters');
118
		}
119
120
		//Update History
121
		//NOTE: To avoid doing another query to grab the last_updated time, we just use time() which <should> get the same thing.
122
		//FIXME: The <preferable> solution here is we'd just check against the last_updated time, but that can have a few issues.
123
		$this->History->updateTitleHistory($titleID, $row->current_chapter, $latestChapter, date('Y-m-d H:i:s'));
124
125
		return (bool) $success;
126
	}
127
128
	public function updateFailedChecksByID(int $titleID) : bool {
129
		$success = $this->db->set('failed_checks', 'failed_checks + 1', FALSE)
130
		                    ->where('id', $titleID)
131
		                    ->update('tracker_titles');
132
133
		return $success;
134
	}
135
136
	/**
137
	 * @param string $site_url
138
	 *
139
	 * @return stdClass|object|null
140
	 */
141 2
	public function getSiteDataFromURL(string $site_url) {
142 2
		$query = $this->db->select('*')
143 2
		                  ->from('tracker_sites')
144 2
		                  ->where('site', $site_url)
145 2
		                  ->get();
146
147 2
		return $query->row();
148
	}
149
}
150