Entries tagged composer

Herausfinden zu welchem Versanddienstleister eine Tracking ID gehört

Posted on 29. Juli 2016 Comments

Wir hatten erst letztens das Problem, dass wir automatisiert Tracking IDs zugespielt bekommen, die von mehreren Versanddienstleistern stammen könnten. Dies ist z. B. beim ERP Plentymarkets der Fall, wenn man Pakete per FBA verschickt. Allerdings nur, wenn die Aufträge nicht von Amazon kommen, sondern ein sog. Multichannel Auftrag vorliegt, also Amazon nur als Logistiker benutzt wird. In der API Rückmeldung steht zwar m.E. der Versanddienstleister, allerdings implementiert Plentymarkets kein Mapping.

Um den richtigen Provider zu finden, ohne jedes mal alle Websites abzuklappern habe ich das PHP Paket shipping-service-providers-check geschrieben und über composer verfügbar gemacht. Es macht automatisiert genau das. Falls es nicht geht, weil z.B. die Flash oder JavaScript im Spiel sind oder es gar keine öffentliche Seite gibt (->Amazon Logistics), kann die Nummer auch anhand des Formats geprüft werden, also mit einem regulärem Ausdruck.

Theoretisch ist es möglich, dass die Tracking ID gleichzeitig bei mehreren Providern für unterschiedliche Sendungen gültig ist. Das ist zugegebenermaßen extrem unwahrscheinlich. Dennoch gibt mein Paket ein Array von Paketdienstleistern, jeweils mit einem Boolean (true/false), zurück.

Code Erklärung

Die Abhängigkeiten sind fabpot/goutte (ein Website scraper) und danielstjules/stringy (für Stringvergleiche). Da ich von PHPs use function Gebrauch mache, ist PHP Version 5.6 notwendig.

Die Klasse Check enthält neben dem Konstruktur, der die TrackingID erwartet, 3 weitere Methoden.

  • getProviders() – gibt alle Provider zurück
  • checkAll($extraProviders) – gibt das eben erwähnte Array zurück. Das hier ist die eigentlich wichtige öffentliche Methode
  • check() – private, wird in einer Schleife von checkAll() aufgerufen

In der Datei default_providers.php sind in einem Array alle implementierten Versanddienstleister aufgeführt. Hier können auch weitere hinzugefügt werden, bzw. in diesem Format an checkAll() übergeben werden. Jeder Dienstleister hat 3 Parameter:

  • base_url – URL, die goutte zusammen mit der tracking ID aufrufen wird
  • filter – HTML Tag auf der Seite, nach dem gesucht bzw. das durchsucht werden soll
  • search_string  – nach diesem String wird in dem HTML Tag gesucht

Der eigentlich wichtige Teil ist deswegen die Methode checkOnline()

$crawler = $this->client->request('GET', $parameters["base_url"] . $this->trackingId);

in dieser Zeile ruft goutte die vorher definierte base_url mit der Tracking ID auf. Die URL muss deshalb im Format http://example.com?tracking_id= vorliegen. Aus der Tracking ID 123456 lautet dann der Aufruf http://example.com?tracking_id=123456.

$crawler->filter($parameters["filter"])->each(function ($node) use ($parameters) {
            if (s($node->text())->contains($parameters["search_string"])) {
                return true;
            }
            return false;

In dem HTML Tag aus dem filter Parameter wird jetzt nach dem String search_string mittels stringys contains() gesucht. Sollte das der Fall sein, wird true zurückgegeben. Da die Funktion each ein Array zurückgibt und es möglich ist, dass das relevante HTML Tag mehrmals vorkommt, wird danach geguckt ob true in diesem array überhaupt vorkommt, auch wenn andere Einträge in diesem Array eben false sind.

return in_array(true, ...)

Dies wird in einer Schleife in checkAll() durchgegangen:

        foreach ($shippingProviders as $shippingProvider => $parameters) {
            $response[$shippingProvider] = $this->check($parameters);
        }

Außerdem ist es durch folgende Zeile möglich einen Versanddienstleister hinzuzufügen oder zu ersetzen (beides passiert durch array_merge())

  if (isset($shippingProviders)) {
            $shippingProviders = array_merge($defaultShippingProviders, $shippingProviders);
        } else {
            $shippingProviders = $defaultShippingProviders;
        }

Die Methode zum Überprüfen des regulärem Ausdrucks checkFormat() ist denkbar simpel:

  boolval(preg_match($parameters["regex"], $this->trackingId))

Da preg_match 1 zurückgibt, falls der Regex zutrifft, und 0 falls nicht (false wenn ein Fehler aufgetreten ist), muss um das Ergebnis noch boolval().

Die Anleitung zum Benutzen des Pakets ist auf GitHub bzw. Packagist. Zur Zeit des Verfassens dieses Artikels sind die folgenden Versanddienstleister implementiert:

  • DHL
  • GLS
  • UPS
  • Hermes
  • Amazon Logistics

Fedex, DPD und TNT gestalten sich schwierig, da diese Informationen per JavaScript nachladen und goutte das nicht beherrscht. Ggf. werde ich nochmal ein npm Paket mit zombie.js und/oder phantomjs schreiben.

Flattr this!

Collect currency exchange rates in a MySQL database with PHP and fixer.io API

Posted on 4. August 2015 Comments

If you work in a company that buys and sells goods in many different currencies, it might be a good idea, to use the latest exchange rates. Also, it might be useful, to store old exchange rates to clarify/verify old business decisions. If once a day is enough for you, fixer.io offers a free simple Rest API. A lot of the code at my work is written in PHP but I usually use the request library in JavaScript and Python, so I’m using it in this example too. A common PHP solution would be guzzle. But first, get composer (the PHP counterpart to npm or pip):

$ curl -sS https://getcomposer.org/installer | php

$ php composer.phar require rmccue/requests

The mysql_ commands are deprecated (and removed in PHP 7), use mysqli or PDO. Also you should use some sort of framework for the database access, like medoo or a proper ORM. This is just proof of concept.


$base = 'EUR';
$request = Requests::get('https://api.fixer.io/latest?base=' . $base, array('Accept' => 'application/json'));
if ($request->status_code == 200) {
$response = json_decode($request->body);
$GBP = $response->rates->GBP;
$CAD = $response->rates->CAD;
$USD = $response->rates->USD;
$NOK = $response->rates->NOK;
$CNY = $response->rates->CNY;
$rBase = mysql_real_escape_string($response->base);
$date = mysql_real_escape_string($response->date);
$currencies = mysql_real_escape_string("1.0, $USD, $GBP, $NOK, $CNY, $CAD");
$qry = "INSERT INTO `exchange_rates_fixerio`(date, base, eur, usd, gbp, nok, cny, cad) VALUES ('$date', '$rBase', $currencies);";
$insert = mysql_query($qry, $mysqlConnection) or print mysql_error();
}

I assume the database connection is defined earlier, there’s lot’s of documentation for that. Because we are from Europe, I chose Euro (EUR) as the base currency. Apart from the get() method, you need nothing else, to send a request. If the request returns an OK(200), the response is read and saved into different variables, e.g. for British Pounds, US Dollar, Canadian Dollar, Chinese Renminbi and Norwegian Krone. Just to make sure we have the right base, it’s also parsed. From there it’s only a simple INSERT INTO (as said before, use a framework for that)

The table could look like this:

CREATE TABLE `echange_rates_fixerio` (
`date` date NOT NULL,
`base` varchar(3) NOT NULL,
`eur` double NOT NULL,
`usd` double NOT NULL,
`gbp` double NOT NULL,
`nok` double NOT NULL,
`cny` double NOT NULL,
`cad` double NOT NULL
)

 

You can also find this code on GitHub.

Flattr this!