Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 235
0.00% covered (danger)
0.00%
0 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
ProductController
0.00% covered (danger)
0.00%
0 / 235
0.00% covered (danger)
0.00%
0 / 14
1722
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 create
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 pushJSONtoAbly
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 deleteProduct
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 getUnfinishedProducts
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 removeProducts
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 removeProduct
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
56
 getProductBySKU
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
 storeProductInDatabase
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
12
 updateProductInDatabase
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 markProductAsSentToShopify
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 postProductToShopify
0.00% covered (danger)
0.00%
0 / 94
0.00% covered (danger)
0.00%
0 / 1
110
 updateInventoryLevels
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 getImages
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace BuyerKiosk\Shopify\Controllers;
4
5use PHPShopify\Exception\ApiException;
6use PHPShopify\Exception\CurlException;
7use BuyerKiosk\Core\Controllers\BaseController;
8use BuyerKiosk\Shopify\Product;
9
10class ProductController extends BaseController {
11    private $store;
12    private $log;
13    private $predis;
14    private $productKey;
15    private $ably;
16    private $shopify;
17    private $db;
18
19    public function __construct($app, \Store $store)
20    {
21        parent::__construct($app);
22        $this->store = $store;
23        $this->log = new \KLogger($_ENV['LOG_DIR']."shopify.log", \KLogger::DEBUG);
24        $this->predis = new \Predis\Client($_ENV['REDIS_URL']);
25        $this->productKey = $store->getTypeNum()."_products";
26        $this->ably = new \Ably\AblyRest(ablyKey);
27
28        $config = array(
29            'ShopUrl' => $this->store->getShopifyURL(),
30            'ApiKey' => $this->store->getShopifyAPIKey(),
31            'Password' => $this->store->getShopifyPassword(),
32        );
33        $this->shopify = new \PHPShopify\ShopifySDK($config);
34        /** @var \PDO $this->db*/
35        $this->db = dbConnectByName($store->getDbName());
36        $this->db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
37    }
38
39    public function create(Product $product) {
40        return $this->storeProductInDatabase($product);
41    }
42    public function pushJSONtoAbly($json) {
43        $channel = $this->ably->channel($this->store->getTypeNum());
44        $channel->publish("shopify_product",$json);
45    }
46    public function deleteProduct($sku) {
47        try {
48            /** @var Product $product */
49            $this->db = dbConnectByName($this->store->getDbName());
50            $stmt = $this->db ->prepare("DELETE FROM shopifyProduct WHERE sku = :sku");
51
52            $stmt->bindValue(":sku", $sku);
53            if($stmt->execute()) {
54                $channel = $this->ably->channel($this->store->getTypeNum());
55                $channel->publish("shopify_product_delete",json_encode(array("sku" => $sku)));
56                return true;
57            }
58        } catch (\PDOException $e) {
59            $this->log->LogError($e->getMessage());
60            return false;
61        }
62        return false;
63    }
64
65    public function getUnfinishedProducts() {
66        $stmt = $this->db->prepare("SELECT * FROM shopifyProduct WHERE sent = 0 ORDER BY id DESC");
67        $stmt->execute();
68        $productsArray = [];
69        while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
70            $tempProduct = new Product();
71            $tempProduct->mapFromRow($row);
72            $tempProduct->added = new \DateTime($row['added'], new \DateTimeZone("UTC"));
73            $tempProduct->added->setTimezone(new \DateTimeZone($this->store->getTimeZone()));
74            $tempProduct->images = $this->getImages($tempProduct);
75            $productsArray[] = $tempProduct;
76        }
77        return $productsArray;
78
79    }
80    public function removeProducts($skuArray) {
81        foreach($skuArray as $sku) {
82            $this->removeProduct($sku);
83            sleep(0.5);
84        }
85    }
86    private function removeProduct($sku) {
87        //We need to get the productID out of the database from the SKU given
88        try {
89            $stmt = $this->db->prepare("SELECT productID FROM shopifyProduct WHERE sku = :sku");
90            $stmt->bindValue(":sku", $sku);
91            if($stmt->execute()) {
92                if($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
93                    if($row['productID'] == NULL) {
94                        $this->log->LogError("Did Not Delete Product [".$sku."]. ProductID was NULL | Store: [".$this->store->getTypeNum()."]");
95                        return false;
96                    }
97                    $this->log->LogDebug("Product ID: ".$row['productID']);
98                    try {
99                        $response = $this->shopify->Product($row['productID'])->delete();
100                    } catch (ApiException $e) {
101                        $this->log->LogError("API Exception: ".$e->getMessage());
102                    } catch (CurlException $e) {
103                        $this->log->LogError("CURL Exception: ".$e->getMessage());
104                    }
105                    $this->log->LogDebug("Deleting Product: [".$row['productID']."] | Store: [".$this->store->getTypeNum()."]");
106                    return true;
107                } else {
108                    $this->log->LogError("SKU Not Found in Database | Store: [".$this->store->getTypeNum()."] for SKU [".$sku."]");
109                }
110            } else {
111                $this->log->LogError("Product Delete Retrieval Fail on Execute| Store: [".$this->store->getTypeNum()."]");
112            }
113        } catch (\PDOException $e) {
114            $this->log->LogError($e->getMessage());
115            return false;
116        }
117        return false;
118    }
119
120    public function getProductBySKU($sku) {
121        try {
122            $stmt = $this->db->prepare("SELECT * FROM shopifyProduct WHERE sku = :sku");
123            $stmt->bindValue(":sku", $sku);
124            if($stmt->execute()) {
125                if($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
126                    $product = new Product();
127                    $product->mapFromRow($row);
128                    return $product;
129                } else {
130                    $this->log->LogError("Get Product By Sku Row Fetch Failed");
131                }
132            } else {
133                $this->log->LogError("Get Product By Sku Execution Failed");
134            }
135        } catch (\PDOException $e) {
136            $this->log->LogError($e->getMessage());
137            return null;
138        }
139        return null;
140    }
141
142    public function storeProductInDatabase(Product $product) {
143        try {
144            $stmt = $this->db ->prepare("INSERT INTO shopifyProduct (productID, sku, title, sent, description, brand, type, tags, color, size, quantity,price, images, added) VALUES (:productID, :sku, :title,0,:description,:brand,:type,:tags,:color,:size,:quantity,:price,:images, CURRENT_TIMESTAMP )");
145            $stmt->bindValue(":productID", $product->productID);
146            $stmt->bindValue(":sku", $product->sku);
147            $stmt->bindValue(":title", $product->title);
148            $stmt->bindValue(":description", $product->description);
149            $stmt->bindValue(":brand", $product->brand);
150            $stmt->bindValue(":type", $product->type);
151            $stmt->bindValue(":tags", $product->tags);
152            $stmt->bindValue(":color", $product->color);
153            $stmt->bindValue(":size", $product->size);
154            $stmt->bindValue(":price", $product->price);
155            $stmt->bindValue(":quantity", $product->quantity);
156            $stmt->bindValue(":images", $product->images);
157            if($stmt->execute()) {
158                return true;
159            } else {
160                $this->log->LogError("Store Product Execution Failed");
161            }
162        } catch (\PDOException $e) {
163            $this->log->LogError($e->getMessage());
164            return false;
165        }
166    }
167    public function updateProductInDatabase(Product $product) {
168        try {
169            $stmt = $this->db ->prepare("UPDATE shopifyProduct SET productID = :productID, sent = :sent, title = :title, description = :description, brand = :brand, `type` = :type, tags = :tags, color = :color, size = :size, price = :price, quantity = :quantity, images = :images WHERE sku = :sku");
170            $stmt->bindValue(":productID", $product->productID);
171            $stmt->bindValue(":sku", $product->sku);
172            $stmt->bindValue(":title", $product->title);
173            $stmt->bindValue(":sent", $product->sent);
174            $stmt->bindValue(":description", $product->description);
175            $stmt->bindValue(":brand", $product->brand);
176            $stmt->bindValue(":type", $product->type);
177            $stmt->bindValue(":tags", $product->tags);
178            $stmt->bindValue(":color", $product->color);
179            $stmt->bindValue(":size", $product->size);
180            $stmt->bindValue(":price", $product->price);
181            $stmt->bindValue(":quantity", $product->quantity);
182            $stmt->bindValue(":images", $product->images);
183            return $stmt->execute();
184        } catch (\PDOException $e) {
185            $this->log->LogError($e->getMessage());
186            return false;
187        }
188    }
189    public function markProductAsSentToShopify(Product $product) {
190        try {
191            $stmt = $this->db ->prepare("UPDATE shopifyProduct SET sent = 1 WHERE sku = :sku");
192            $stmt->bindValue(":sku", $product->sku);
193            return $stmt->execute();
194        } catch (\PDOException $e) {
195            $this->log->LogError($e->getMessage());
196            return false;
197        }
198    }
199    public function postProductToShopify(Product $product) {
200        $error = array("success" => 0, "message" => "An unknown error occurred. Please try again","inventoryID" => null);
201        $imageURLs = $this->getImages($product);
202        if(strlen($product->size) < 1) {
203            $productArray = array (
204                "title" => $product->title,
205                "product_type" => $product->type,
206                "options"=> array(
207                    array("name" => "Color"),
208                ),
209                "body_html" => $product->description,
210                "variants" => [
211                    [
212                        "sku" => $product->sku,
213                        "fulfillment_service" => "manual",
214                        "option1" => $product->color,
215                        "presentment_prices" => [
216                            "price" => [
217                                "currency_code"=> "USD",
218                                "amount" => $product->price
219                            ],
220                            "compare_at_price"=> null
221                        ],
222                        "requires_shipping" => true,
223                        "taxable" => true,
224                        "title" => $product->title,
225                        "vendor" => $product->brand,
226                        "inventory_management" => "shopify",
227                    ]
228                ],
229                "images"=> $imageURLs,
230                "tags"=>$product->tags,
231                "vendor"=>$product->brand
232
233            );
234        } else {
235            $productArray = array (
236                "title" => $product->title,
237                "product_type" => $product->type,
238                "options"=> array(
239                    array("name" => "Color"),
240                    array("name" => "Size")
241                ),
242                "body_html" => $product->description,
243                "variants" => [
244                    [
245                        "sku" => $product->sku,
246                        "fulfillment_service" => "manual",
247                        "option1" => $product->color,
248                        "option2" => $product->size,
249                        "price" => $product->price,
250                        "presentment_prices" => [
251                            "price" => [
252                                "currency_code"=> "USD",
253                                "amount" => $product->price
254                            ],
255                            "compare_at_price"=> null
256                        ],
257                        "requires_shipping" => true,
258                        "taxable" => true,
259                        "title" => $product->title,
260                        "vendor" => $product->brand,
261                        "inventory_management" => "shopify",
262                    ]
263                ],
264                "images"=> $imageURLs,
265                "tags"=>$product->tags,
266                "vendor"=>$product->brand
267
268            );
269        }
270        try {
271            $result = $this->shopify->Product->post($productArray);
272            $inventoryID = $result['variants'][0]['inventory_item_id'];
273            if($inventoryID > 0) {
274                $inventoryResult = $this->updateInventoryLevels($inventoryID);
275                $count = 0;
276                while(isset($inventoryResult["errors"]) && $count < 5) {
277                    sleep(1);
278                    $inventoryResult = $this->updateInventoryLevels($inventoryID);
279                    $count ++;
280                }
281                $product->sent = 1;
282                $product->productID = $result['id'];
283                $this->updateProductInDatabase($product);
284                if(!isset($inventoryResult["errors"])) {
285                    $error = array("success" => 1, "message" => "Successfully Uploaded Product", "inventoryID" => $inventoryID);
286                } else {
287                    $error = array("success" => 1, "message" => "Successfully Uploaded Product, But inventory update failed after 5 retries. Please update inventory quantity manually", "inventoryID" => $inventoryID);
288                }
289            }
290        } catch (ApiException $e) {
291            $this->log->LogError("API Exception: ".$e->getMessage());
292            $error = array("success" => 0, "message" => "An API Error Occurred. Please Try Again.","inventoryID" => null);
293        } catch (CurlException $e) {
294            $this->log->LogError("CURL Exception: ".$e->getMessage());
295            $error = array("success" => 0, "message" => "An CURL Error Occurred. Please Try Again.","inventoryID" => null);
296        } catch (\PDOException $e) {
297            $this->log->LogError("PDO Exception: ".$e->getMessage());
298            $error = array("success" => 0, "message" => "An PDO Error Occurred. Please Try Again.","inventoryID" => null);
299        }
300        if($error["success"] == 1) {
301            $channel = $this->ably->channel($this->store->getTypeNum());
302            $channel->publish("shopify_product_success",json_encode($product));
303        }
304        return $error;
305    }
306    public function updateInventoryLevels($inventoryID) {
307        $locationID = $this->store->getShopifyLocationID();
308        $inventoryLevel = array(
309            "location_id" => $locationID,
310            "inventory_item_id" => $inventoryID,
311            "available_adjustment" => 1
312        );
313        return $this->shopify->InventoryLevel->adjust($inventoryLevel);
314    }
315    public function getImages($product) {
316        $ds          = DIRECTORY_SEPARATOR;
317        $storeFolder = '../../../public_html/upload/shopify/'.$this->store->getTypeNum();
318        $publicFolder = '/upload/shopify/'.$this->store->getTypeNum();
319        $urls = [];
320        $this->log->LogDebug($storeFolder.$ds.$product->sku.".jpg");
321        $files = glob(dirname( __FILE__ ).$ds.$storeFolder.$ds.$product->sku."*.{jpg,png}", GLOB_BRACE);
322        $this->log->LogDebug(count($files));
323        foreach($files as $file) {
324            $file = str_replace($ds.$storeFolder.$ds,"",$file);
325            $file = str_replace(dirname( __FILE__ ),"",$file);
326            $tempURL = "http://".serverName.$publicFolder."/".$file;
327            $this->log->LogDebug($tempURL);
328            $urls[] = array("src" => $tempURL);
329        }
330        $this->log->LogDebug(json_encode($urls));
331        return $urls;
332    }
333}