- added CzechRegisterSearchService

- oasis RouteServiceProvider.php
- oasis admincontroller
This commit is contained in:
Peter Papp
2021-03-22 08:19:50 +01:00
parent 4999b23474
commit 80b24cd753
5 changed files with 352 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Oasis;
use App\Http\Controllers\Controller;
use App\Services\CzechRegisterSearchService;
use Illuminate\Http\Request;
class AdminController extends Controller
{
/**
* Get company details from czech company register
*
* @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function get_company_details()
{
$api = resolve(CzechRegisterSearchService::class);
$result = $api->findByIco(
request()->get('ico')
);
if (empty($result)) {
return response('Not Found', 404);
}
return response($result[0], 200);
}
}

View File

@@ -40,6 +40,8 @@ class RouteServiceProvider extends ServiceProvider
*/
public function map()
{
$this->mapOasisRoutes();
$this->mapApiRoutes();
$this->mapShareRoutes();
@@ -100,6 +102,14 @@ class RouteServiceProvider extends ServiceProvider
->group(base_path('routes/api.php'));
}
protected function mapOasisRoutes()
{
Route::prefix('api/oasis')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/oasis.php'));
}
protected function mapShareRoutes()
{
Route::prefix('api')

View File

@@ -0,0 +1,255 @@
<?php
/**
* Parser pre vyhladavanie a vypis z obchodneho registra ČR
* Lookup service for Czech commercial register (www.justice.cz)
*
* Version 1.0.0 (released 05.12.2019)
* (c) 2019 lubosdz@gmail.com
*
* ------------------------------------------------------------------
* Disclaimer / Prehlásenie:
* Kód poskytnutý je bez záruky a môže kedykoľvek prestať fungovať.
* Jeho funkčnosť je striktne naviazaná na generovanú štruktúru HTML elementov.
* Autor nie je povinný udržiavať kód aktuálny a funkčný, ani neposkytuje ku nemu žiadnu podporu.
* Autor nezodpovedá za nesprávne použitie kódu.
* ------------------------------------------------------------------
*
* Usage example:
* --------------
* $connector = new ConnectorJusticeCz;
* $out = $connector->findByNazev('auto');
* $out = $connector->findByIco('44315945');
* echo ''.print_r($out, 1).'';
*/
namespace App\Services;
class CzechRegisterSearchService
{
/**
* @var string Target server URL base
*/
const URL_SERVER = 'https://or.justice.cz/ias/ui/rejstrik-$firma';
/**
* @var int Max. dlzka nazvu pre autocomplete options
*/
public $labelMaxChars = 30;
/**
* Vyhlada zoznam subjektov podla presneho ICO, ciastkove ICO nie je povolene
* @param string $ico 8-miestne cislo, e.g. 29243831 or 64612023
*/
public function findByIco($ico)
{
$response = [];
$ico = preg_replace('/[^\d]/', '', $ico);
if (preg_match('/^\d{8}$/', $ico)) {
$url = self::URL_SERVER . '?ico=' . $ico;
$response = file_get_contents($url);
$response = self::extractSubjects($response);
}
return $response;
}
/**
* Vyhlada zoznam subjektov podla ciastkoveho nazvu
* @param string $nazev , e.g. "pojisteni"
*/
public function findByNazev($nazev)
{
$response = [];
if ($nazev) {
$nazev = trim($nazev);
$url = self::URL_SERVER . '?nazev=' . urlencode($nazev);
$response = file_get_contents($url);
$response = self::extractSubjects($response);
}
return $response;
}
/**
* Vyhlada zoznam subjektov podla presneho ICO, ciastkove ICO nie je povolene
* @param string $ico 8-miestne cislo, e.g. 12345678
*/
public function getDetailByICO($ico)
{
$response = [];
$ico = preg_replace('/[^\d]/', '', $ico);
if (preg_match('/^\d{8}$/', $ico)) {
$url = self::URL_SERVER . '?ico=' . $ico;
$response = file_get_contents($url);
if ($response) {
$response = self::extractSubjects($response);
if (!empty($response[0])) {
$response = $response[0];
}
}
}
return $response;
}
/**
* Return matched formatted for autocomplete dropdown list
* @param string $term Searched matching string
* @param int how many items to return, 1 - 50 (justice vrati max. 50 zaznamov)
*/
public function findForAutocomplete($term, $size = 15)
{
$out = [];
$size = intval($size);
if ($term && $size > 0 && mb_strlen($term, 'utf-8') >= 3) {
// Justice vrati vysledok pre min. 3 znaky
if (preg_match('/^\d{8}$/', $term)) {
$subjects = $this->findByIco($term);
} else {
$subjects = $this->findByNazev($term);
}
}
if (!empty($subjects) && is_array($subjects)) {
$subjects = array_slice($subjects, 0, $size); // return first $size matches
foreach ($subjects as &$subject) {
$subject['shortname'] = $subject['name'];
// cut off too long names
if (mb_strlen($subject['shortname'], 'utf-8') > $this->labelMaxChars) {
$subject['shortname'] = mb_substr($subject['shortname'], 0, $this->labelMaxChars - 3, 'utf-8') . ' ..';
}
}
foreach ($subjects as $subject) {
if (!empty($subject['ico'])) {
$out[] = [
'value' => $subject['ico'],
'label' => "{$subject['shortname']} (IČO: {$subject['ico']})",
];
}
}
}
return $out;
}
/**
* Vrati zoznam najdenych subjektov s udajmi
* @param string $html HTML response zo servera justice.cz
* @return [name, ico, city, addr_streetnr, addr_city, addr_zip, ..]
*/
protected static function extractSubjects($html)
{
// ensure valid XHTML markup
if (!extension_loaded('tidy')) {
throw new \Exception('Missing extension [tidy].');
}
$tidy = new \tidy();
$html = $tidy->repairString($html, array(
'output-xhtml' => true,
'show-body-only' => true,
), 'utf8');
// purify whitespaces - vkladaju \n alebo
$html = strtr($html, [
' ' => ' ',
]);
$html = preg_replace('/\s+/', ' ', $html);
// load XHTML into DOM document
$xml = new \DOMDocument('1.0', 'utf-8');
$xml->loadXML($html);
$xpath = new \DOMXPath($xml);
$rows = $xpath->query('//table[@class="result-details"]/tbody');
$out = [];
if ($rows->length) {
foreach ($rows as $row) {
// Nazev
$nodeList = $xpath->query("./tr[1]/td[1]", $row);
if (!$nodeList->length) {
continue; // nazev je povinny
}
$name = $nodeList->item(0)->nodeValue;
$name = preg_replace('/\s+/', ' ', $name); // viacnasobne inside spaces
// ICO
$nodeList = $xpath->query("./tr[1]/td[2]", $row);
$ico = $nodeList->length ? $nodeList->item(0)->nodeValue : '';
// adresa - neda sa spolahnut na poradie prvkov :-(
$city = '';
$nodeList = $xpath->query("./tr[3]/td[1]", $row);
if ($nodeList->length) {
$addr = trim($nodeList->item(0)->nodeValue);
if (preg_match('/,\s*(\d{3} ?\d{2})\s+(.+)$/', $addr, $match)) {
// Příborská 597, Místek, 738 01 Frýdek-Místek - nazov obce za PSC, prva je ulice a cislo
$city = $addr_city = $match[2];
list($addr_streetnr) = explode(',', $addr);
$addr_zip = $match[1];
} elseif (preg_match('/,\s*PSČ\s+(\d{3} ?\d{2})$/', $addr, $match)) {
// Řevnice, ČSLA 118, okres Praha-západ, PSČ 25230 - PSC na konci, obec je prva, ulice a cislo druha
list($city, $addr_streetnr) = explode(',', $addr);
$addr_city = $city;
$addr_zip = $match[1];
} elseif (!preg_match('/\d{3} ?\d{2}/', $addr, $match)) {
// Ústí nad Labem, Masarykova 74 - bez PSC - obec, ulice a cislo
$addr_streetnr = $addr_zip = '';
if (false !== strpos($addr, ',')) {
list($city, $addr_streetnr) = explode(',', $addr);
} else {
list($city) = explode(',', $addr);
}
$addr_city = $city;
}
// "Praha 10 - Dolní Měcholupy" -> Praha 10, pozn: Frydek-Mistek nema medzeru okolo pomlcky
// whoops, avsak ani Ostrana-Hontice a dalsie .. :-( Pre city potrebujeme kratky nazov do 10-15 pismen
list($city) = explode('-', $city);
// Praha 5 -> Praha
$city = preg_replace('/\d/', '', $city);
// viacnasobne spaces
$city = preg_replace('/\s+/', ' ', $city);
}
$out[] = [
'name' => self::trimQuotes($name),
'ico' => preg_replace('/[^\d]/', '', $ico),
'city' => self::trimQuotes($city),
// pre polia s adresou konzistentne so smartform naseptavacem
'addr_city' => self::trimQuotes($addr_city),
'addr_zip' => preg_replace('/[^\d]/', '', $addr_zip),
'addr_streetnr' => self::trimQuotes($addr_streetnr),
// len pre kontrolu - plna povodna adresa
'addr_full' => self::trimQuotes($addr),
];
}
}
return $out;
}
/**
* Vyhodi quotes z textu, aby neposkodilo HTML atributy
* @param string $s
*/
protected static function trimQuotes($s)
{
return trim(strtr($s, ['"' => '', "'" => '']));
}
}

8
routes/oasis.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
use App\Http\Controllers\Oasis\AdminController;
Route::group(['prefix' => 'admin'], function () {
Route::get('/company-details', [AdminController::class, 'get_company_details']);
});

View File

@@ -0,0 +1,49 @@
<?php
namespace Tests\Feature\Oasis;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;
class OasisAdminTest extends TestCase
{
use DatabaseMigrations;
/**
* @test
*/
public function it_get_register_by_ico()
{
$response = [
"name" => "GDPR Cloud Solution, s.r.o.",
"ico" => "08995281",
"city" => "Praha",
"addr_city" => "Praha 5",
"addr_zip" => "15900",
"addr_streetnr" => "Zbraslavská 12/11",
"addr_full" => "Zbraslavská 12/11, Malá Chuchle, 159 00 Praha 5",
];
Http::fake([
'https://or.justice.cz/ias/ui/rejstrik-08995281' => Http::response($response, 200),
]);
$this->getJson('/api/oasis/admin/company-details?ico=08995281')
->assertStatus(200)
->assertExactJson($response);
}
/**
* @test
*/
public function it_try_to_get_register_by_ico_with_not_found_result()
{
Http::fake([
'https://or.justice.cz/ias/ui/rejstrik-0899528199' => Http::response([], 200),
]);
$this->getJson('/api/oasis/admin/company-details?ico=0899528199')
->assertNotFound();
}
}