Completed
Push — master ( cc0ba8...2e633f )
by Zhmayev
01:28
created

Entities::checkParams()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 6
Ratio 60 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 2
nop 2
dl 6
loc 10
rs 9.2
c 0
b 0
f 0
1
<?php
2
/**
3
* Vtiger Web Services PHP Client Library
4
*
5
* The MIT License (MIT)
6
*
7
* Copyright (c) 2015, Zhmayev Yaroslav <[email protected]>
8
*
9
* Permission is hereby granted, free of charge, to any person obtaining a copy
10
* of this software and associated documentation files (the "Software"), to deal
11
* in the Software without restriction, including without limitation the rights
12
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
* copies of the Software, and to permit persons to whom the Software is
14
* furnished to do so, subject to the following conditions:
15
*
16
* The above copyright notice and this permission notice shall be included in
17
* all copies or substantial portions of the Software.
18
*
19
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
* THE SOFTWARE.
26
*
27
* @author    Zhmayev Yaroslav <[email protected]>
28
* @copyright 2015-2016 Zhmayev Yaroslav
29
* @license   The MIT License (MIT)
30
*/
31
32
namespace Salaros\Vtiger\VTWSCLib;
33
34
use Salaros\Vtiger\VTWSCLib\WSClient;
35
36
/**
37
* Vtiger Web Services PHP Client Session class
38
*
39
* Class Entities
40
* @package Salaros\Vtiger\VTWSCLib
41
*/
42
class Entities
43
{
44
    private $wsClient = null;
45
46
    /**
47
     * Class constructor
48
     * @param string $wsClient  Parent WSClient instance
49
     */
50
    public function __construct($wsClient)
51
    {
52
        $this->wsClient = $wsClient;
53
    }
54
55
    /**
56
     * Retrieves an entity by ID
57
     * @param  string  $moduleName The name of the module / entity type
58
     * @param  string  $entityID The ID of the entity to retrieve
59
     * @return array   $select  The list of fields to select (defaults to SQL-like '*' - all the fields)
60
     * @return boolean  Entity data
61
     */
62
    public function findOneByID($moduleName, $entityID, array $select = [ ])
63
    {
64
        return $this->findOne($moduleName, [ 'id' => $entityID ], $select);
65
    }
66
67
    /**
68
     * Retrieve the entity matching a list of constraints
69
     * @param  string  $moduleName   The name of the module / entity type
70
     * @param  array   $params  Data used to find a matching entry
71
     * @return array   $select  The list of fields to select (defaults to SQL-like '*' - all the fields)
72
     * @return integer  The matching record
73
     */
74 View Code Duplication
    public function findOne($moduleName, array $params, array $select = [ ])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
75
    {
76
        $records = $this->findMany($moduleName, $params, $select, 1);
77
        if (false === $records || !isset($records[ 0 ])) {
78
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Salaros\Vtiger\VTWSCLib\Entities::findOne of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
79
        }
80
        return $records[ 0 ];
81
    }
82
83
    /**
84
     * Retrieves the ID of the entity matching a list of constraints + prepends '<module_id>x' string to it
85
     * @param  string $moduleName   The name of the module / entity type
86
     * @param  array   $params  Data used to find a matching entry
87
     * @return integer  Type ID (a numeric ID + '<module_id>x')
88
     */
89 View Code Duplication
    public function getID($moduleName, array $params)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
90
    {
91
        $record = $this->findOne($moduleName, $params, [ 'id' ]);
92
        if (false === $record || !isset($record[ 'id' ]) || empty($record[ 'id' ])) {
93
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Salaros\Vtiger\VTWSCLib\Entities::getID of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
94
        }
95
        return $record[ 'id' ];
96
    }
97
98
    /**
99
     * Retrieve a numeric ID of the entity matching a list of constraints
100
     * @param  string  $moduleName   The name of the module / entity type
101
     * @param  array   $params  Data used to find a matching entry
102
     * @return integer  Numeric ID
103
     */
104
    public function getNumericID($moduleName, array $params)
105
    {
106
        $entityID = $this->getID($moduleName, $params);
107
        $entityIDParts = explode('x', $entityID, 2);
108
        return (is_array($entityIDParts) && count($entityIDParts) === 2)
109
            ? intval($entityIDParts[ 1 ])
110
            : -1;
111
    }
112
113
    /**
114
     * Creates an entity for the giving module
115
     * @param  string  $moduleName   Name of the module / entity type for which the entry has to be created
116
     * @param  array  $params Entity data
117
     * @return array  Entity creation results
118
     */
119
    public function createOne($moduleName, array $params)
120
    {
121
        if (!is_assoc_array($params)) {
122
            throw new WSException(
123
                "You have to specify at least one search parameter (prop => value) 
124
                in order to be able to create an entity"
125
            );
126
        }
127
128
        // Assign record to logged in user if not specified
129
        if (!isset($params[ 'assigned_user_id' ])) {
130
            $currentUser = $this->wsClient->getCurrentUser();
0 ignored issues
show
Bug introduced by
The method getCurrentUser cannot be called on $this->wsClient (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
131
            $params[ 'assigned_user_id' ] = $currentUser[ 'id' ];
132
        }
133
134
        $requestData = [
135
            'elementType' => $moduleName,
136
            'element'     => json_encode($params)
137
        ];
138
139
        return $this->wsClient->invokeOperation('create', $requestData);
0 ignored issues
show
Bug introduced by
The method invokeOperation cannot be called on $this->wsClient (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
140
    }
141
142
    /**
143
     * Updates an entity
144
     * @param  string $moduleName   The name of the module / entity type
145
     * @param  array $params Entity data
146
     * @return array  Entity update result
147
     */
148
    public function updateOne($moduleName, $entityID, array $params)
149
    {
150
        if (!is_assoc_array($params)) {
151
            throw new WSException(
152
                "You have to specify at least one search parameter (prop => value) 
153
                in order to be able to update the entity(ies)"
154
            );
155
        }
156
157
        // Fail if no ID was supplied
158
        if (empty($entityID)) {
159
            throw new WSException("The list of contraints must contain a valid ID");
160
        }
161
162
        // Preprend so-called moduleid if needed
163
        $entityID = $this->wsClient->modules->getTypedID($moduleName, $entityID);
164
165
        // Check if the entity exists + retrieve its data so it can be used below
166
        $entityData = $this->findOneByID($moduleName, $entityID);
167
        if ($entityData === false && !is_array($entityData)) {
168
            throw new WSException("Such entity doesn't exist, so it cannot be updated");
169
        }
170
171
        // The new data overrides the existing one needed to provide
172
        // mandatory field values to WS 'update' operation
173
        $params = array_merge(
174
            $entityData,
175
            $params
176
        );
177
178
        $requestData = [
179
            'elementType' => $moduleName,
180
            'element'     => json_encode($params)
181
        ];
182
183
        return $this->wsClient->invokeOperation('update', $requestData);
0 ignored issues
show
Bug introduced by
The method invokeOperation cannot be called on $this->wsClient (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
184
    }
185
186
    /**
187
     * Provides entity removal functionality
188
     * @param  string $moduleName   The name of the module / entity type
189
     * @param  string $entityID The ID of the entity to delete
190
     * @return array  Removal status object
191
     */
192
    public function deleteOne($moduleName, $entityID)
193
    {
194
        // Preprend so-called moduleid if needed
195
        $entityID = $this->wsClient->modules->getTypedID($moduleName, $entityID);
196
        return $this->wsClient->invokeOperation('delete', [ 'id' => $entityID ]);
0 ignored issues
show
Bug introduced by
The method invokeOperation cannot be called on $this->wsClient (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
197
    }
198
199
    /**
200
     * Retrieves multiple records using module name and a set of constraints
201
     * @param  string   $moduleName  The name of the module / entity type
202
     * @param  array    $params  Data used to find matching entries
203
     * @return array    $select  The list of fields to select (defaults to SQL-like '*' - all the fields)
204
     * @return integer      $limit  limit the list of entries to N records (acts like LIMIT in SQL)
205
     * @return bool|array  The array containing matching entries or false if nothing was found
206
     */
207
    public function findMany($moduleName, array $params, array $select = [ ], $limit = 0)
208
    {
209
        if (!is_assoc_array($params)) {
210
            throw new WSException(
211
                "You have to specify at least one search parameter (prop => value) 
212
                in order to be able to retrieve entity(ies)"
213
            );
214
        }
215
216
        // Builds the query
217
        $query = self::getQueryString($moduleName, $params, $select, $limit);
218
219
        // Run the query
220
        $records = $this->wsClient->runQuery($query);
0 ignored issues
show
Bug introduced by
The method runQuery cannot be called on $this->wsClient (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
221
        if (false === $records || !is_array($records) || (count($records) <= 0)) {
222
            return false;
223
        }
224
225
        return $records;
226
    }
227
228
    /**
229
     * Sync will return a sync result object containing details of changes after modifiedTime
230
     * @param  integer [$modifiedTime = null]    The date of the first change
231
     * @param  string [$moduleName = null]   The name of the module / entity type
232
     * @return array  Sync result object
233
     */
234
    public function sync($modifiedTime = null, $moduleName = null)
235
    {
236
        $modifiedTime = (empty($modifiedTime))
237
            ? strtotime('today midnight')
238
            : intval($modifiedTime);
239
240
        $requestData = [
241
            'modifiedTime' => $modifiedTime
242
        ];
243
244
        if (!empty($moduleName)) {
245
            $requestData[ 'elementType' ] = $moduleName;
246
        }
247
248
        return $this->wsClient->invokeOperation('sync', $requestData, true);
0 ignored issues
show
Bug introduced by
The method invokeOperation cannot be called on $this->wsClient (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
249
    }
250
251
    /**
252
     * Builds the query using the supplied parameters
253
     * @access public
254
     * @static
255
     * @param  string   $moduleName  The name of the module / entity type
256
     * @param  array    $params  Data used to find matching entries
257
     * @return string    $select  The list of fields to select (defaults to SQL-like '*' - all the fields)
258
     * @return string      $limit  limit the list of entries to N records (acts like LIMIT in SQL)
259
     * @return string   The query build out of the supplied parameters
260
     */
261
    public static function getQueryString($moduleName, array $params, array $select = [ ], $limit = 0)
262
    {
263
        $criteria = array();
264
        $select = (empty($select)) ? '*' : implode(',', $select);
265
        $query = sprintf("SELECT %s FROM $moduleName WHERE ", $select);
266
        foreach ($params as $param => $value) {
267
            $criteria[ ] = "{$param} LIKE '{$value}'";
268
        }
269
270
        $query .= implode(" AND ", $criteria);
271
        if (intval($limit) > 0) {
272
            $query .= sprintf(" LIMIT %s", intval($limit));
273
        }
274
        return $query;
275
    }
276
}
277