diff --git a/.gitignore b/.gitignore
index a5bf6a6..22296d0 100755
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,7 @@ com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
+composer.lock
+boxfile.yml
+build/
+tests/_?db-*/
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
new file mode 100644
index 0000000..d09f774
--- /dev/null
+++ b/.scrutinizer.yml
@@ -0,0 +1,35 @@
+build:
+ nodes:
+ analysis:
+ project_setup:
+ override:
+ - 'true'
+ tests:
+ override:
+ - php-scrutinizer-run
+ - command: phpcs-run
+ use_website_config: false
+
+ tests:
+ environment:
+ docker: true
+ php:
+ version: 7.2
+ dependencies:
+ before:
+ - sudo service mysql stop
+ - sudo service postgresql stop
+ - make -C tests/ start_db_mysql start_db_postgres oV=8.0 pV=11
+ tests:
+ override:
+ - command: ./vendor/bin/phpunit --coverage-clover=my-coverage-file
+ coverage:
+ file: my-coverage-file
+ format: php-clover
+
+checks:
+ php: true
+
+filter:
+ excluded_paths:
+ - tests/*
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..1523830
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,67 @@
+language: php
+
+sudo: required
+
+services:
+ - docker
+
+php:
+ - '5.6'
+ - '7.0'
+ - '7.1'
+ - '7.2'
+ - '7.3'
+ - hhvm
+ - nightly
+
+env:
+ - MYSQL_VERSION=5.7 PG_VERSION=9.1
+ - MYSQL_VERSION=8.0 PG_VERSION=9.2
+ - MARIA_VERSION=5.5 PG_VERSION=9.3
+ - MARIA_VERSION=10.0 PG_VERSION=9.4
+ - MARIA_VERSION=10.1 PG_VERSION=9.5
+ - MARIA_VERSION=10.2 PG_VERSION=9.6
+ - MARIA_VERSION=10.3 PG_VERSION=10
+ - MARIA_VERSION=10.4 PG_VERSION=11
+
+matrix:
+ allow_failures:
+ - php: '5.6'
+ - php: hhvm
+ - php: nightly
+
+before_install:
+ - sudo /etc/init.d/mysql stop
+ - |
+ if [ -n "${MYSQL_VERSION}" ]
+ then
+ make -C tests/ start_db_mysql oV=${MYSQL_VERSION}
+ else
+ make -C tests/ start_db_maria mV=${MARIA_VERSION}
+ fi
+ - sudo /etc/init.d/postgresql stop
+ - make -C tests/ start_db_postgres pV=${PG_VERSION}
+
+install:
+ - travis_retry composer install
+
+before_script:
+ - mkdir -p build/logs
+ - echo -n Ensuring MySQL/MariaDB is ready; ./tests/wait_container spatial-mysql ; echo
+ - echo -n Ensuring PostgreSQL DB is ready; ./tests/wait_container spatial-postgres ; echo
+
+script:
+ - vendor/bin/phpunit --coverage-clover build/logs/clover.xml
+
+after_script:
+ - |
+ if [ -n "${MYSQL_VERSION}" ]
+ then
+ make -C tests/ rm_db_mysql oV=${MYSQL_VERSION}
+ else
+ make -C tests/ rm_db_maria mV=${MARIA_VERSION}
+ fi
+ - make -C tests/ rm_db_postgres pV=${PG_VERSION}
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/README.md b/README.md
index 6e17167..69a0234 100755
--- a/README.md
+++ b/README.md
@@ -1,6 +1,15 @@
# Laravel Spatial extension
-This package is fully untested, undocumented and unstable and is a combination of the two great packages:
+[](https://packagist.org/packages/apptimists/laravel-spatial)
+[](https://packagist.org/packages/apptimists/laravel-spatial)
+[](https://packagist.org/packages/apptimists/laravel-spatial)
+[](https://packagist.org/packages/apptimists/laravel-spatial)
+
+[](https://github.com/apptimists/laravel-spatial)
+[](https://codecov.io/gh/apptimists/laravel-spatial)
+[](https://scrutinizer-ci.com/g/apptimists/laravel-spatial/)
+
+This package is fully undocumented and unstable, and is a combination of the two great packages:
- [Laravel PostGIS extension](https://github.com/njbarrett/laravel-postgis)
- [Laravel MySQL spatial extension](https://github.com/grimzy/laravel-mysql-spatial)
@@ -24,13 +33,13 @@ If you try using it on a shared host which is not fulfilling those requirements,
We use the [GeoJson PHP Library](http://jmikola.github.io/geojson/) for describing spatial fields as GeoJSON object, e.g.:
```
use GeoJSON\Geometry\Point;
-
+
...
-
+
$eloquent = new MyModel();
$eloquent->location = new Point([49.7, 6.9]);
-
+
...
-
+
$eloquent->save();
```
diff --git a/composer.json b/composer.json
index 9184f50..7b2ebce 100755
--- a/composer.json
+++ b/composer.json
@@ -11,11 +11,14 @@
],
"require": {
"php": ">=5.6.4",
+ "doctrine/dbal": "^2.5",
+ "funiq/geophp": "dev-master",
"illuminate/database": "^5.3",
- "jmikola/geojson": "^1.0",
- "phayes/geophp": "^1.2"
+ "jmikola/geojson": "^1.0"
},
"require-dev": {
+ "laravel/browser-kit-testing": "^2.0",
+ "laravel/laravel": "^5.3",
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "~5.0"
},
@@ -24,6 +27,12 @@
"LaravelSpatial\\": "src/"
}
},
+ "autoload-dev": {
+ "classmap": [
+ "tests/Unit",
+ "tests/Integration"
+ ]
+ },
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
diff --git a/composer.lock b/composer.lock
deleted file mode 100644
index 411ce7f..0000000
--- a/composer.lock
+++ /dev/null
@@ -1,2026 +0,0 @@
-{
- "_readme": [
- "This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
- "This file is @generated automatically"
- ],
- "content-hash": "b626ea4ba0a5337f082eb6601770692e",
- "packages": [
- {
- "name": "doctrine/inflector",
- "version": "v1.1.0",
- "source": {
- "type": "git",
- "url": "https://github.com/doctrine/inflector.git",
- "reference": "90b2128806bfde671b6952ab8bea493942c1fdae"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae",
- "reference": "90b2128806bfde671b6952ab8bea493942c1fdae",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.2"
- },
- "require-dev": {
- "phpunit/phpunit": "4.*"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.1.x-dev"
- }
- },
- "autoload": {
- "psr-0": {
- "Doctrine\\Common\\Inflector\\": "lib/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Roman Borschel",
- "email": "roman@code-factory.org"
- },
- {
- "name": "Benjamin Eberlei",
- "email": "kontakt@beberlei.de"
- },
- {
- "name": "Guilherme Blanco",
- "email": "guilhermeblanco@gmail.com"
- },
- {
- "name": "Jonathan Wage",
- "email": "jonwage@gmail.com"
- },
- {
- "name": "Johannes Schmitt",
- "email": "schmittjoh@gmail.com"
- }
- ],
- "description": "Common String Manipulations with regard to casing and singular/plural rules.",
- "homepage": "http://www.doctrine-project.org",
- "keywords": [
- "inflection",
- "pluralize",
- "singularize",
- "string"
- ],
- "time": "2015-11-06T14:35:42+00:00"
- },
- {
- "name": "illuminate/container",
- "version": "v5.4.19",
- "source": {
- "type": "git",
- "url": "https://github.com/illuminate/container.git",
- "reference": "50aa19491d478edd907d1f67e0928944e8b2dcb5"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/illuminate/container/zipball/50aa19491d478edd907d1f67e0928944e8b2dcb5",
- "reference": "50aa19491d478edd907d1f67e0928944e8b2dcb5",
- "shasum": ""
- },
- "require": {
- "illuminate/contracts": "5.4.*",
- "php": ">=5.6.4"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.4-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Illuminate\\Container\\": ""
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Taylor Otwell",
- "email": "taylor@laravel.com"
- }
- ],
- "description": "The Illuminate Container package.",
- "homepage": "https://laravel.com",
- "time": "2017-04-16T13:32:45+00:00"
- },
- {
- "name": "illuminate/contracts",
- "version": "v5.4.19",
- "source": {
- "type": "git",
- "url": "https://github.com/illuminate/contracts.git",
- "reference": "ab2825726bee46a67c8cc66789852189dbef74a9"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/illuminate/contracts/zipball/ab2825726bee46a67c8cc66789852189dbef74a9",
- "reference": "ab2825726bee46a67c8cc66789852189dbef74a9",
- "shasum": ""
- },
- "require": {
- "php": ">=5.6.4"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.4-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Illuminate\\Contracts\\": ""
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Taylor Otwell",
- "email": "taylor@laravel.com"
- }
- ],
- "description": "The Illuminate Contracts package.",
- "homepage": "https://laravel.com",
- "time": "2017-03-29T13:17:47+00:00"
- },
- {
- "name": "illuminate/database",
- "version": "v5.4.19",
- "source": {
- "type": "git",
- "url": "https://github.com/illuminate/database.git",
- "reference": "890564c6b84bcb2b45d41d3da072fabf422c07f5"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/illuminate/database/zipball/890564c6b84bcb2b45d41d3da072fabf422c07f5",
- "reference": "890564c6b84bcb2b45d41d3da072fabf422c07f5",
- "shasum": ""
- },
- "require": {
- "illuminate/container": "5.4.*",
- "illuminate/contracts": "5.4.*",
- "illuminate/support": "5.4.*",
- "nesbot/carbon": "~1.20",
- "php": ">=5.6.4"
- },
- "suggest": {
- "doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.5).",
- "fzaninotto/faker": "Required to use the eloquent factory builder (~1.4).",
- "illuminate/console": "Required to use the database commands (5.4.*).",
- "illuminate/events": "Required to use the observers with Eloquent (5.4.*).",
- "illuminate/filesystem": "Required to use the migrations (5.4.*).",
- "illuminate/pagination": "Required to paginate the result set (5.4.*)."
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.4-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Illuminate\\Database\\": ""
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Taylor Otwell",
- "email": "taylor@laravel.com"
- }
- ],
- "description": "The Illuminate Database package.",
- "homepage": "https://laravel.com",
- "keywords": [
- "database",
- "laravel",
- "orm",
- "sql"
- ],
- "time": "2017-04-11T22:53:18+00:00"
- },
- {
- "name": "illuminate/support",
- "version": "v5.4.19",
- "source": {
- "type": "git",
- "url": "https://github.com/illuminate/support.git",
- "reference": "b8cb37e15331c59da51c8ee5838038baa22d7955"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/illuminate/support/zipball/b8cb37e15331c59da51c8ee5838038baa22d7955",
- "reference": "b8cb37e15331c59da51c8ee5838038baa22d7955",
- "shasum": ""
- },
- "require": {
- "doctrine/inflector": "~1.0",
- "ext-mbstring": "*",
- "illuminate/contracts": "5.4.*",
- "paragonie/random_compat": "~1.4|~2.0",
- "php": ">=5.6.4"
- },
- "replace": {
- "tightenco/collect": "self.version"
- },
- "suggest": {
- "illuminate/filesystem": "Required to use the composer class (5.2.*).",
- "symfony/process": "Required to use the composer class (~3.2).",
- "symfony/var-dumper": "Required to use the dd function (~3.2)."
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.4-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Illuminate\\Support\\": ""
- },
- "files": [
- "helpers.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Taylor Otwell",
- "email": "taylor@laravel.com"
- }
- ],
- "description": "The Illuminate Support package.",
- "homepage": "https://laravel.com",
- "time": "2017-04-09T14:34:57+00:00"
- },
- {
- "name": "jmikola/geojson",
- "version": "1.0.2",
- "source": {
- "type": "git",
- "url": "https://github.com/jmikola/geojson.git",
- "reference": "6ec3016cc0215667b7775f6ead7bd0337ad66eee"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/jmikola/geojson/zipball/6ec3016cc0215667b7775f6ead7bd0337ad66eee",
- "reference": "6ec3016cc0215667b7775f6ead7bd0337ad66eee",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3"
- },
- "require-dev": {
- "phpunit/phpunit": "~3.7"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0-dev"
- }
- },
- "autoload": {
- "psr-0": {
- "GeoJson\\": "src/"
- },
- "classmap": [
- "stubs/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Jeremy Mikola",
- "email": "jmikola@gmail.com"
- }
- ],
- "description": "GeoJSON implementation for PHP",
- "homepage": "https://github.com/jmikola/geojson",
- "keywords": [
- "geo",
- "geojson",
- "geospatial"
- ],
- "time": "2015-09-27T15:35:21+00:00"
- },
- {
- "name": "nesbot/carbon",
- "version": "1.22.1",
- "source": {
- "type": "git",
- "url": "https://github.com/briannesbitt/Carbon.git",
- "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
- "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.0",
- "symfony/translation": "~2.6 || ~3.0"
- },
- "require-dev": {
- "friendsofphp/php-cs-fixer": "~2",
- "phpunit/phpunit": "~4.0 || ~5.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.23-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Carbon\\": "src/Carbon/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Brian Nesbitt",
- "email": "brian@nesbot.com",
- "homepage": "http://nesbot.com"
- }
- ],
- "description": "A simple API extension for DateTime.",
- "homepage": "http://carbon.nesbot.com",
- "keywords": [
- "date",
- "datetime",
- "time"
- ],
- "time": "2017-01-16T07:55:07+00:00"
- },
- {
- "name": "paragonie/random_compat",
- "version": "v2.0.10",
- "source": {
- "type": "git",
- "url": "https://github.com/paragonie/random_compat.git",
- "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d",
- "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d",
- "shasum": ""
- },
- "require": {
- "php": ">=5.2.0"
- },
- "require-dev": {
- "phpunit/phpunit": "4.*|5.*"
- },
- "suggest": {
- "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
- },
- "type": "library",
- "autoload": {
- "files": [
- "lib/random.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Paragon Initiative Enterprises",
- "email": "security@paragonie.com",
- "homepage": "https://paragonie.com"
- }
- ],
- "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
- "keywords": [
- "csprng",
- "pseudorandom",
- "random"
- ],
- "time": "2017-03-13T16:27:32+00:00"
- },
- {
- "name": "phayes/geophp",
- "version": "1.2",
- "source": {
- "type": "git",
- "url": "https://github.com/phayes/geoPHP.git",
- "reference": "015404e85b602e0df1f91441f8db0f9e98f7e567"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phayes/geoPHP/zipball/015404e85b602e0df1f91441f8db0f9e98f7e567",
- "reference": "015404e85b602e0df1f91441f8db0f9e98f7e567",
- "shasum": ""
- },
- "require-dev": {
- "phpunit/phpunit": "4.1.*"
- },
- "type": "library",
- "autoload": {
- "classmap": [
- "geoPHP.inc"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "GPL-2 or New-BSD"
- ],
- "authors": [
- {
- "name": "Patrick Hayes"
- }
- ],
- "description": "GeoPHP is a open-source native PHP library for doing geometry operations. It is written entirely in PHP and can therefore run on shared hosts. It can read and write a wide variety of formats: WKT (including EWKT), WKB (including EWKB), GeoJSON, KML, GPX, GeoRSS). It works with all Simple-Feature geometries (Point, LineString, Polygon, GeometryCollection etc.) and can be used to get centroids, bounding-boxes, area, and a wide variety of other useful information.",
- "homepage": "https://github.com/phayes/geoPHP",
- "time": "2014-12-02T06:11:22+00:00"
- },
- {
- "name": "symfony/polyfill-mbstring",
- "version": "v1.3.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4",
- "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3"
- },
- "suggest": {
- "ext-mbstring": "For best performance"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.3-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Mbstring\\": ""
- },
- "files": [
- "bootstrap.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill for the Mbstring extension",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "mbstring",
- "polyfill",
- "portable",
- "shim"
- ],
- "time": "2016-11-14T01:06:16+00:00"
- },
- {
- "name": "symfony/translation",
- "version": "v3.2.8",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/translation.git",
- "reference": "f4a04d2df710f81515df576b2de06bdeee518b83"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/f4a04d2df710f81515df576b2de06bdeee518b83",
- "reference": "f4a04d2df710f81515df576b2de06bdeee518b83",
- "shasum": ""
- },
- "require": {
- "php": ">=5.5.9",
- "symfony/polyfill-mbstring": "~1.0"
- },
- "conflict": {
- "symfony/config": "<2.8"
- },
- "require-dev": {
- "psr/log": "~1.0",
- "symfony/config": "~2.8|~3.0",
- "symfony/intl": "^2.8.18|^3.2.5",
- "symfony/yaml": "~2.8|~3.0"
- },
- "suggest": {
- "psr/log": "To use logging capability in translator",
- "symfony/config": "",
- "symfony/yaml": ""
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.2-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\Translation\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony Translation Component",
- "homepage": "https://symfony.com",
- "time": "2017-04-12T14:13:17+00:00"
- }
- ],
- "packages-dev": [
- {
- "name": "doctrine/instantiator",
- "version": "1.0.5",
- "source": {
- "type": "git",
- "url": "https://github.com/doctrine/instantiator.git",
- "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
- "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3,<8.0-DEV"
- },
- "require-dev": {
- "athletic/athletic": "~0.1.8",
- "ext-pdo": "*",
- "ext-phar": "*",
- "phpunit/phpunit": "~4.0",
- "squizlabs/php_codesniffer": "~2.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Marco Pivetta",
- "email": "ocramius@gmail.com",
- "homepage": "http://ocramius.github.com/"
- }
- ],
- "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
- "homepage": "https://github.com/doctrine/instantiator",
- "keywords": [
- "constructor",
- "instantiate"
- ],
- "time": "2015-06-14T21:17:01+00:00"
- },
- {
- "name": "hamcrest/hamcrest-php",
- "version": "v1.2.2",
- "source": {
- "type": "git",
- "url": "https://github.com/hamcrest/hamcrest-php.git",
- "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c",
- "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.2"
- },
- "replace": {
- "cordoval/hamcrest-php": "*",
- "davedevelopment/hamcrest-php": "*",
- "kodova/hamcrest-php": "*"
- },
- "require-dev": {
- "phpunit/php-file-iterator": "1.3.3",
- "satooshi/php-coveralls": "dev-master"
- },
- "type": "library",
- "autoload": {
- "classmap": [
- "hamcrest"
- ],
- "files": [
- "hamcrest/Hamcrest.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD"
- ],
- "description": "This is the PHP port of Hamcrest Matchers",
- "keywords": [
- "test"
- ],
- "time": "2015-05-11T14:41:42+00:00"
- },
- {
- "name": "mockery/mockery",
- "version": "0.9.9",
- "source": {
- "type": "git",
- "url": "https://github.com/mockery/mockery.git",
- "reference": "6fdb61243844dc924071d3404bb23994ea0b6856"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/mockery/mockery/zipball/6fdb61243844dc924071d3404bb23994ea0b6856",
- "reference": "6fdb61243844dc924071d3404bb23994ea0b6856",
- "shasum": ""
- },
- "require": {
- "hamcrest/hamcrest-php": "~1.1",
- "lib-pcre": ">=7.0",
- "php": ">=5.3.2"
- },
- "require-dev": {
- "phpunit/phpunit": "~4.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "0.9.x-dev"
- }
- },
- "autoload": {
- "psr-0": {
- "Mockery": "library/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Pádraic Brady",
- "email": "padraic.brady@gmail.com",
- "homepage": "http://blog.astrumfutura.com"
- },
- {
- "name": "Dave Marshall",
- "email": "dave.marshall@atstsolutions.co.uk",
- "homepage": "http://davedevelopment.co.uk"
- }
- ],
- "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.",
- "homepage": "http://github.com/padraic/mockery",
- "keywords": [
- "BDD",
- "TDD",
- "library",
- "mock",
- "mock objects",
- "mockery",
- "stub",
- "test",
- "test double",
- "testing"
- ],
- "time": "2017-02-28T12:52:32+00:00"
- },
- {
- "name": "myclabs/deep-copy",
- "version": "1.6.1",
- "source": {
- "type": "git",
- "url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102",
- "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102",
- "shasum": ""
- },
- "require": {
- "php": ">=5.4.0"
- },
- "require-dev": {
- "doctrine/collections": "1.*",
- "phpunit/phpunit": "~4.1"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "DeepCopy\\": "src/DeepCopy/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "description": "Create deep copies (clones) of your objects",
- "homepage": "https://github.com/myclabs/DeepCopy",
- "keywords": [
- "clone",
- "copy",
- "duplicate",
- "object",
- "object graph"
- ],
- "time": "2017-04-12T18:52:22+00:00"
- },
- {
- "name": "phpdocumentor/reflection-common",
- "version": "1.0",
- "source": {
- "type": "git",
- "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
- "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
- "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
- "shasum": ""
- },
- "require": {
- "php": ">=5.5"
- },
- "require-dev": {
- "phpunit/phpunit": "^4.6"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "phpDocumentor\\Reflection\\": [
- "src"
- ]
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Jaap van Otterdijk",
- "email": "opensource@ijaap.nl"
- }
- ],
- "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
- "homepage": "http://www.phpdoc.org",
- "keywords": [
- "FQSEN",
- "phpDocumentor",
- "phpdoc",
- "reflection",
- "static analysis"
- ],
- "time": "2015-12-27T11:43:31+00:00"
- },
- {
- "name": "phpdocumentor/reflection-docblock",
- "version": "3.1.1",
- "source": {
- "type": "git",
- "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
- "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e",
- "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e",
- "shasum": ""
- },
- "require": {
- "php": ">=5.5",
- "phpdocumentor/reflection-common": "^1.0@dev",
- "phpdocumentor/type-resolver": "^0.2.0",
- "webmozart/assert": "^1.0"
- },
- "require-dev": {
- "mockery/mockery": "^0.9.4",
- "phpunit/phpunit": "^4.4"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "phpDocumentor\\Reflection\\": [
- "src/"
- ]
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Mike van Riel",
- "email": "me@mikevanriel.com"
- }
- ],
- "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
- "time": "2016-09-30T07:12:33+00:00"
- },
- {
- "name": "phpdocumentor/type-resolver",
- "version": "0.2.1",
- "source": {
- "type": "git",
- "url": "https://github.com/phpDocumentor/TypeResolver.git",
- "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
- "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
- "shasum": ""
- },
- "require": {
- "php": ">=5.5",
- "phpdocumentor/reflection-common": "^1.0"
- },
- "require-dev": {
- "mockery/mockery": "^0.9.4",
- "phpunit/phpunit": "^5.2||^4.8.24"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "phpDocumentor\\Reflection\\": [
- "src/"
- ]
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Mike van Riel",
- "email": "me@mikevanriel.com"
- }
- ],
- "time": "2016-11-25T06:54:22+00:00"
- },
- {
- "name": "phpspec/prophecy",
- "version": "v1.7.0",
- "source": {
- "type": "git",
- "url": "https://github.com/phpspec/prophecy.git",
- "reference": "93d39f1f7f9326d746203c7c056f300f7f126073"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073",
- "reference": "93d39f1f7f9326d746203c7c056f300f7f126073",
- "shasum": ""
- },
- "require": {
- "doctrine/instantiator": "^1.0.2",
- "php": "^5.3|^7.0",
- "phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
- "sebastian/comparator": "^1.1|^2.0",
- "sebastian/recursion-context": "^1.0|^2.0|^3.0"
- },
- "require-dev": {
- "phpspec/phpspec": "^2.5|^3.2",
- "phpunit/phpunit": "^4.8 || ^5.6.5"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.6.x-dev"
- }
- },
- "autoload": {
- "psr-0": {
- "Prophecy\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Konstantin Kudryashov",
- "email": "ever.zet@gmail.com",
- "homepage": "http://everzet.com"
- },
- {
- "name": "Marcello Duarte",
- "email": "marcello.duarte@gmail.com"
- }
- ],
- "description": "Highly opinionated mocking framework for PHP 5.3+",
- "homepage": "https://github.com/phpspec/prophecy",
- "keywords": [
- "Double",
- "Dummy",
- "fake",
- "mock",
- "spy",
- "stub"
- ],
- "time": "2017-03-02T20:05:34+00:00"
- },
- {
- "name": "phpunit/php-code-coverage",
- "version": "4.0.8",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
- "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
- "shasum": ""
- },
- "require": {
- "ext-dom": "*",
- "ext-xmlwriter": "*",
- "php": "^5.6 || ^7.0",
- "phpunit/php-file-iterator": "^1.3",
- "phpunit/php-text-template": "^1.2",
- "phpunit/php-token-stream": "^1.4.2 || ^2.0",
- "sebastian/code-unit-reverse-lookup": "^1.0",
- "sebastian/environment": "^1.3.2 || ^2.0",
- "sebastian/version": "^1.0 || ^2.0"
- },
- "require-dev": {
- "ext-xdebug": "^2.1.4",
- "phpunit/phpunit": "^5.7"
- },
- "suggest": {
- "ext-xdebug": "^2.5.1"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.0.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
- "role": "lead"
- }
- ],
- "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
- "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
- "keywords": [
- "coverage",
- "testing",
- "xunit"
- ],
- "time": "2017-04-02T07:44:40+00:00"
- },
- {
- "name": "phpunit/php-file-iterator",
- "version": "1.4.2",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
- "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.4.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
- "role": "lead"
- }
- ],
- "description": "FilterIterator implementation that filters files based on a list of suffixes.",
- "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
- "keywords": [
- "filesystem",
- "iterator"
- ],
- "time": "2016-10-03T07:40:28+00:00"
- },
- {
- "name": "phpunit/php-text-template",
- "version": "1.2.1",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/php-text-template.git",
- "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
- "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3"
- },
- "type": "library",
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
- }
- ],
- "description": "Simple template engine.",
- "homepage": "https://github.com/sebastianbergmann/php-text-template/",
- "keywords": [
- "template"
- ],
- "time": "2015-06-21T13:50:34+00:00"
- },
- {
- "name": "phpunit/php-timer",
- "version": "1.0.9",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/php-timer.git",
- "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
- "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
- "shasum": ""
- },
- "require": {
- "php": "^5.3.3 || ^7.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
- "role": "lead"
- }
- ],
- "description": "Utility class for timing",
- "homepage": "https://github.com/sebastianbergmann/php-timer/",
- "keywords": [
- "timer"
- ],
- "time": "2017-02-26T11:10:40+00:00"
- },
- {
- "name": "phpunit/php-token-stream",
- "version": "1.4.11",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/php-token-stream.git",
- "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7",
- "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7",
- "shasum": ""
- },
- "require": {
- "ext-tokenizer": "*",
- "php": ">=5.3.3"
- },
- "require-dev": {
- "phpunit/phpunit": "~4.2"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.4-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- }
- ],
- "description": "Wrapper around PHP's tokenizer extension.",
- "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
- "keywords": [
- "tokenizer"
- ],
- "time": "2017-02-27T10:12:30+00:00"
- },
- {
- "name": "phpunit/phpunit",
- "version": "5.7.19",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/69c4f49ff376af2692bad9cebd883d17ebaa98a1",
- "reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1",
- "shasum": ""
- },
- "require": {
- "ext-dom": "*",
- "ext-json": "*",
- "ext-libxml": "*",
- "ext-mbstring": "*",
- "ext-xml": "*",
- "myclabs/deep-copy": "~1.3",
- "php": "^5.6 || ^7.0",
- "phpspec/prophecy": "^1.6.2",
- "phpunit/php-code-coverage": "^4.0.4",
- "phpunit/php-file-iterator": "~1.4",
- "phpunit/php-text-template": "~1.2",
- "phpunit/php-timer": "^1.0.6",
- "phpunit/phpunit-mock-objects": "^3.2",
- "sebastian/comparator": "^1.2.4",
- "sebastian/diff": "~1.2",
- "sebastian/environment": "^1.3.4 || ^2.0",
- "sebastian/exporter": "~2.0",
- "sebastian/global-state": "^1.1",
- "sebastian/object-enumerator": "~2.0",
- "sebastian/resource-operations": "~1.0",
- "sebastian/version": "~1.0.3|~2.0",
- "symfony/yaml": "~2.1|~3.0"
- },
- "conflict": {
- "phpdocumentor/reflection-docblock": "3.0.2"
- },
- "require-dev": {
- "ext-pdo": "*"
- },
- "suggest": {
- "ext-xdebug": "*",
- "phpunit/php-invoker": "~1.1"
- },
- "bin": [
- "phpunit"
- ],
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.7.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
- }
- ],
- "description": "The PHP Unit Testing framework.",
- "homepage": "https://phpunit.de/",
- "keywords": [
- "phpunit",
- "testing",
- "xunit"
- ],
- "time": "2017-04-03T02:22:27+00:00"
- },
- {
- "name": "phpunit/phpunit-mock-objects",
- "version": "3.4.3",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
- "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3ab72b65b39b491e0c011e2e09bb2206c2aa8e24",
- "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24",
- "shasum": ""
- },
- "require": {
- "doctrine/instantiator": "^1.0.2",
- "php": "^5.6 || ^7.0",
- "phpunit/php-text-template": "^1.2",
- "sebastian/exporter": "^1.2 || ^2.0"
- },
- "conflict": {
- "phpunit/phpunit": "<5.4.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^5.4"
- },
- "suggest": {
- "ext-soap": "*"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.2.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
- "role": "lead"
- }
- ],
- "description": "Mock Object library for PHPUnit",
- "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
- "keywords": [
- "mock",
- "xunit"
- ],
- "time": "2016-12-08T20:27:08+00:00"
- },
- {
- "name": "sebastian/code-unit-reverse-lookup",
- "version": "1.0.1",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
- "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
- "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
- "shasum": ""
- },
- "require": {
- "php": "^5.6 || ^7.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^5.7 || ^6.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- }
- ],
- "description": "Looks up which function or method a line of code belongs to",
- "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
- "time": "2017-03-04T06:30:41+00:00"
- },
- {
- "name": "sebastian/comparator",
- "version": "1.2.4",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
- "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3",
- "sebastian/diff": "~1.2",
- "sebastian/exporter": "~1.2 || ~2.0"
- },
- "require-dev": {
- "phpunit/phpunit": "~4.4"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.2.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
- {
- "name": "Volker Dusch",
- "email": "github@wallbash.com"
- },
- {
- "name": "Bernhard Schussek",
- "email": "bschussek@2bepublished.at"
- },
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- }
- ],
- "description": "Provides the functionality to compare PHP values for equality",
- "homepage": "http://www.github.com/sebastianbergmann/comparator",
- "keywords": [
- "comparator",
- "compare",
- "equality"
- ],
- "time": "2017-01-29T09:50:25+00:00"
- },
- {
- "name": "sebastian/diff",
- "version": "1.4.1",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
- "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3"
- },
- "require-dev": {
- "phpunit/phpunit": "~4.8"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.4-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Kore Nordmann",
- "email": "mail@kore-nordmann.de"
- },
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- }
- ],
- "description": "Diff implementation",
- "homepage": "https://github.com/sebastianbergmann/diff",
- "keywords": [
- "diff"
- ],
- "time": "2015-12-08T07:14:41+00:00"
- },
- {
- "name": "sebastian/environment",
- "version": "2.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
- "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
- "shasum": ""
- },
- "require": {
- "php": "^5.6 || ^7.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^5.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- }
- ],
- "description": "Provides functionality to handle HHVM/PHP environments",
- "homepage": "http://www.github.com/sebastianbergmann/environment",
- "keywords": [
- "Xdebug",
- "environment",
- "hhvm"
- ],
- "time": "2016-11-26T07:53:53+00:00"
- },
- {
- "name": "sebastian/exporter",
- "version": "2.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4",
- "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3",
- "sebastian/recursion-context": "~2.0"
- },
- "require-dev": {
- "ext-mbstring": "*",
- "phpunit/phpunit": "~4.4"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
- {
- "name": "Volker Dusch",
- "email": "github@wallbash.com"
- },
- {
- "name": "Bernhard Schussek",
- "email": "bschussek@2bepublished.at"
- },
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- },
- {
- "name": "Adam Harvey",
- "email": "aharvey@php.net"
- }
- ],
- "description": "Provides the functionality to export PHP variables for visualization",
- "homepage": "http://www.github.com/sebastianbergmann/exporter",
- "keywords": [
- "export",
- "exporter"
- ],
- "time": "2016-11-19T08:54:04+00:00"
- },
- {
- "name": "sebastian/global-state",
- "version": "1.1.1",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
- "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3"
- },
- "require-dev": {
- "phpunit/phpunit": "~4.2"
- },
- "suggest": {
- "ext-uopz": "*"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- }
- ],
- "description": "Snapshotting of global state",
- "homepage": "http://www.github.com/sebastianbergmann/global-state",
- "keywords": [
- "global state"
- ],
- "time": "2015-10-12T03:26:01+00:00"
- },
- {
- "name": "sebastian/object-enumerator",
- "version": "2.0.1",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/object-enumerator.git",
- "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7",
- "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7",
- "shasum": ""
- },
- "require": {
- "php": ">=5.6",
- "sebastian/recursion-context": "~2.0"
- },
- "require-dev": {
- "phpunit/phpunit": "~5"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- }
- ],
- "description": "Traverses array structures and object graphs to enumerate all referenced objects",
- "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
- "time": "2017-02-18T15:18:39+00:00"
- },
- {
- "name": "sebastian/recursion-context",
- "version": "2.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a",
- "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3"
- },
- "require-dev": {
- "phpunit/phpunit": "~4.4"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- },
- {
- "name": "Adam Harvey",
- "email": "aharvey@php.net"
- }
- ],
- "description": "Provides functionality to recursively process PHP variables",
- "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
- "time": "2016-11-19T07:33:16+00:00"
- },
- {
- "name": "sebastian/resource-operations",
- "version": "1.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/resource-operations.git",
- "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
- "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
- "shasum": ""
- },
- "require": {
- "php": ">=5.6.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- }
- ],
- "description": "Provides a list of PHP built-in functions that operate on resources",
- "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
- "time": "2015-07-28T20:34:47+00:00"
- },
- {
- "name": "sebastian/version",
- "version": "2.0.1",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/version.git",
- "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
- "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
- "shasum": ""
- },
- "require": {
- "php": ">=5.6"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
- }
- ],
- "description": "Library that helps with managing the version number of Git-hosted PHP projects",
- "homepage": "https://github.com/sebastianbergmann/version",
- "time": "2016-10-03T07:35:21+00:00"
- },
- {
- "name": "symfony/yaml",
- "version": "v3.2.8",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/yaml.git",
- "reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/acec26fcf7f3031e094e910b94b002fa53d4e4d6",
- "reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6",
- "shasum": ""
- },
- "require": {
- "php": ">=5.5.9"
- },
- "require-dev": {
- "symfony/console": "~2.8|~3.0"
- },
- "suggest": {
- "symfony/console": "For validating YAML files using the lint command"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.2-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\Yaml\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony Yaml Component",
- "homepage": "https://symfony.com",
- "time": "2017-05-01T14:55:58+00:00"
- },
- {
- "name": "webmozart/assert",
- "version": "1.2.0",
- "source": {
- "type": "git",
- "url": "https://github.com/webmozart/assert.git",
- "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f",
- "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f",
- "shasum": ""
- },
- "require": {
- "php": "^5.3.3 || ^7.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^4.6",
- "sebastian/version": "^1.0.1"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.3-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Webmozart\\Assert\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Bernhard Schussek",
- "email": "bschussek@gmail.com"
- }
- ],
- "description": "Assertions to validate method input/output with nice error messages.",
- "keywords": [
- "assert",
- "check",
- "validate"
- ],
- "time": "2016-11-23T20:04:58+00:00"
- }
- ],
- "aliases": [],
- "minimum-stability": "stable",
- "stability-flags": [],
- "prefer-stable": false,
- "prefer-lowest": false,
- "platform": {
- "php": ">=5.6.4"
- },
- "platform-dev": []
-}
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 0000000..b8b688f
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,7 @@
+
+
+ .
+
+
+
+
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..e15cb4f
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,42 @@
+
+
+
+
+ ./tests/Unit
+
+
+ ./tests/Integration
+
+
+
+
+
+
+
+
+ ./src
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Doctrine/Geometry.php b/src/Doctrine/Geometry.php
index b3d556f..540de89 100755
--- a/src/Doctrine/Geometry.php
+++ b/src/Doctrine/Geometry.php
@@ -4,6 +4,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
+use Illuminate\Support\Fluent;
class Geometry extends Type
{
@@ -11,7 +12,7 @@ class Geometry extends Type
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
- return 'geometry';
+ return \DB::connection()->getSchemaGrammar()->typeGeometry(new Fluent);
}
public function getName()
diff --git a/src/Doctrine/GeometryCollection.php b/src/Doctrine/GeometryCollection.php
index f095f7c..41435f6 100755
--- a/src/Doctrine/GeometryCollection.php
+++ b/src/Doctrine/GeometryCollection.php
@@ -4,6 +4,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
+use Illuminate\Support\Fluent;
class GeometryCollection extends Type
{
@@ -11,7 +12,7 @@ class GeometryCollection extends Type
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
- return 'geometrycollection';
+ return \DB::connection()->getSchemaGrammar()->typeGeometrycollection(new Fluent);
}
public function getName()
diff --git a/src/Doctrine/LineString.php b/src/Doctrine/LineString.php
index a92e075..30a2acb 100755
--- a/src/Doctrine/LineString.php
+++ b/src/Doctrine/LineString.php
@@ -4,6 +4,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
+use Illuminate\Support\Fluent;
class LineString extends Type
{
@@ -11,7 +12,7 @@ class LineString extends Type
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
- return 'linestring';
+ return \DB::connection()->getSchemaGrammar()->typeLinestring(new Fluent);
}
public function getName()
diff --git a/src/Doctrine/MultiLineString.php b/src/Doctrine/MultiLineString.php
index c4b1f5a..2653de9 100755
--- a/src/Doctrine/MultiLineString.php
+++ b/src/Doctrine/MultiLineString.php
@@ -4,6 +4,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
+use Illuminate\Support\Fluent;
class MultiLineString extends Type
{
@@ -11,7 +12,7 @@ class MultiLineString extends Type
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
- return 'multilinestring';
+ return \DB::connection()->getSchemaGrammar()->typeMultilinestring(new Fluent);
}
public function getName()
diff --git a/src/Doctrine/MultiPoint.php b/src/Doctrine/MultiPoint.php
index e8147bb..2bc1701 100755
--- a/src/Doctrine/MultiPoint.php
+++ b/src/Doctrine/MultiPoint.php
@@ -4,6 +4,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
+use Illuminate\Support\Fluent;
class MultiPoint extends Type
{
@@ -11,7 +12,7 @@ class MultiPoint extends Type
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
- return 'multipoint';
+ return \DB::connection()->getSchemaGrammar()->typeMultipoint(new Fluent);
}
public function getName()
diff --git a/src/Doctrine/MultiPolygon.php b/src/Doctrine/MultiPolygon.php
index cb95b5a..1c74510 100755
--- a/src/Doctrine/MultiPolygon.php
+++ b/src/Doctrine/MultiPolygon.php
@@ -4,6 +4,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
+use Illuminate\Support\Fluent;
class MultiPolygon extends Type
{
@@ -11,7 +12,7 @@ class MultiPolygon extends Type
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
- return 'multipolygon';
+ return \DB::connection()->getSchemaGrammar()->typeMultipolygon(new Fluent);
}
public function getName()
diff --git a/src/Doctrine/Point.php b/src/Doctrine/Point.php
index bf466f5..f4f261d 100755
--- a/src/Doctrine/Point.php
+++ b/src/Doctrine/Point.php
@@ -4,6 +4,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
+use Illuminate\Support\Fluent;
class Point extends Type
{
@@ -11,7 +12,7 @@ class Point extends Type
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
- return 'point';
+ return \DB::connection()->getSchemaGrammar()->typePoint(new Fluent);
}
public function getName()
diff --git a/src/Doctrine/Polygon.php b/src/Doctrine/Polygon.php
index cb898df..7239c74 100755
--- a/src/Doctrine/Polygon.php
+++ b/src/Doctrine/Polygon.php
@@ -4,6 +4,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
+use Illuminate\Support\Fluent;
class Polygon extends Type
{
@@ -11,7 +12,7 @@ class Polygon extends Type
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
- return 'polygon';
+ return \DB::connection()->getSchemaGrammar()->typePolygon(new Fluent);
}
public function getName()
diff --git a/src/Eloquent/Builder.php b/src/Eloquent/Builder.php
index 78b97b0..7c69671 100755
--- a/src/Eloquent/Builder.php
+++ b/src/Eloquent/Builder.php
@@ -3,7 +3,7 @@
namespace LaravelSpatial\Eloquent;
use GeoJson\Geometry\Geometry;
-use geoPHP;
+use geoPHP\geoPHP;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
class Builder extends EloquentBuilder
@@ -12,8 +12,8 @@ public function update(array $values)
{
foreach ($values as $key => &$value) {
if ($value instanceof Geometry) {
- $geometry = geoPHP::load((object)$value->jsonSerialize(), 'json');
- $value = $this->getQuery()->raw('ST_GeomFromText(\'' . $geometry->out('wkt') . '\')');
+ $wkt = geoPHP::load(json_decode(json_encode($value->jsonSerialize()), false), 'json')->out('wkt');
+ $value = $this->getQuery()->raw("ST_GeomFromText('{$wkt}')");
}
}
diff --git a/src/Eloquent/SpatialTrait.php b/src/Eloquent/SpatialTrait.php
index 415efa4..82e4fe5 100755
--- a/src/Eloquent/SpatialTrait.php
+++ b/src/Eloquent/SpatialTrait.php
@@ -4,15 +4,55 @@
use GeoJson\GeoJson;
use GeoJSON\Geometry\Geometry;
-use geoPHP;
+use geoPHP\geoPHP;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
+use Illuminate\Database\MySqlConnection;
+use Illuminate\Database\PostgresConnection;
use LaravelSpatial\Exceptions\SpatialFieldsNotDefinedException;
+use LaravelSpatial\Exceptions\UnknownSpatialRelationFunction;
-
+/**
+ * Trait SpatialTrait.
+ *
+ * @property array $attributes
+ * @method static distance($geometryColumn, $geometry, $distance)
+ * @method static distanceExcludingSelf($geometryColumn, $geometry, $distance)
+ * @method static distanceSphere($geometryColumn, $geometry, $distance)
+ * @method static distanceSphereExcludingSelf($geometryColumn, $geometry, $distance)
+ * @method static comparison($geometryColumn, $geometry, $relationship)
+ * @method static within($geometryColumn, $polygon)
+ * @method static crosses($geometryColumn, $geometry)
+ * @method static contains($geometryColumn, $geometry)
+ * @method static disjoint($geometryColumn, $geometry)
+ * @method static equals($geometryColumn, $geometry)
+ * @method static intersects($geometryColumn, $geometry)
+ * @method static overlaps($geometryColumn, $geometry)
+ * @method static doesTouch($geometryColumn, $geometry)
+ */
trait SpatialTrait
{
+ /*
+ * The attributes that are spatial representations.
+ * To use this Trait, add the following array to the model class
+ *
+ * @var array
+ *
+ * protected $spatialFields = [];
+ */
+
public $geometries = [];
+ protected $stRelations = [
+ 'Within',
+ 'Crosses',
+ 'Contains',
+ 'Disjoint',
+ 'Equals',
+ 'Intersects',
+ 'Overlaps',
+ 'Touches',
+ ];
+
/**
* Create a new Eloquent query builder for the model.
*
@@ -31,13 +71,17 @@ public function setRawAttributes(array $attributes, $sync = false)
foreach ($attributes as $attribute => &$value) {
if (in_array($attribute, $spatial_fields) && is_string($value) && strlen($value) >= 15) {
// MySQL adds 4 NULL bytes at the start of the binary
- $prefix = "\0\0\0\0";
- if (substr($value, 0, strlen($prefix)) == $prefix) {
- $value = substr($value, strlen($prefix));
+ if ($this->getConnection() instanceof MySqlConnection && substr($value, 0, 4) == "\0\0\0\0") {
+ $value = substr($value, 4);
+ } elseif ($this->getConnection() instanceof PostgresConnection) {
+ $value = pack('H*', $value);
}
- $geometry = geoPHP::load($value, 'wkb');
- $value = GeoJson::jsonUnserialize(json_decode($geometry->out('json')));
+ try {
+ $value = GeoJson::jsonUnserialize(json_decode(geoPHP::load($value, 'wkb')->out('json')));
+ } catch (\Exception $e) {
+ throw new \Exception("Can't parse WKB {$value}: {$e->getMessage()}", $e->getCode(), $e);
+ }
}
}
@@ -46,20 +90,25 @@ public function setRawAttributes(array $attributes, $sync = false)
public function getSpatialFields()
{
- if (property_exists($this, 'spatialFields')) {
+ if (property_exists($this, 'spatialFields') && !empty($this->spatialFields)) {
return $this->spatialFields;
} else {
throw new SpatialFieldsNotDefinedException(__CLASS__ . ' has to define $spatialFields');
}
}
+ protected function toWkt(Geometry $value)
+ {
+ return ($this->getConnection() instanceof PostgresConnection ? 'SRID=4326;' : '') .
+ geoPHP::load(json_decode(json_encode($value->jsonSerialize()), false), 'json')->out('wkt');
+ }
+
protected function performInsert(EloquentBuilder $query, array $options = [])
{
foreach ($this->attributes as $key => $value) {
- if ($value instanceof Geometry) {
- $geometry = geoPHP::load((object)$value->jsonSerialize(), 'json');
+ if ($value instanceof Geometry && $this->isColumnAllowed($key)) {
$this->geometries[$key] = $value; // Preserve the geometry objects prior to the insert
- $this->attributes[$key] = $this->getConnection()->raw('ST_GeomFromText(\'' . $geometry->out('wkt') . '\')');
+ $this->attributes[$key] = $this->getConnection()->raw("ST_GeomFromText('{$this->toWkt($value)}')");
}
}
@@ -71,4 +120,167 @@ protected function performInsert(EloquentBuilder $query, array $options = [])
return $insert; // Return the result of the parent insert
}
+
+ public function isColumnAllowed($geometryColumn)
+ {
+ if (!in_array($geometryColumn, $this->getSpatialFields())) {
+ throw new SpatialFieldsNotDefinedException();
+ }
+
+ return true;
+ }
+
+ public function scopeDistance($query, $geometryColumn, $geometry, $distance)
+ {
+ if ($this->isColumnAllowed($geometryColumn)) {
+ $geometryColumn .= $this->getConnection() instanceof PostgresConnection ? '::geometry' : '';
+ $query->whereRaw("ST_Distance({$geometryColumn}, ST_GeomFromText(?)) <= ?", [
+ $this->toWkt($geometry),
+ $distance,
+ ]);
+ }
+
+ return $query;
+ }
+
+ public function scopeDistanceExcludingSelf($query, $geometryColumn, $geometry, $distance)
+ {
+ if ($this->isColumnAllowed($geometryColumn)) {
+ $query = $this->scopeDistance($query, $geometryColumn, $geometry, $distance);
+
+ $geometryColumn .= $this->getConnection() instanceof PostgresConnection ? '::geometry' : '';
+ $query->whereRaw("ST_Distance({$geometryColumn}, ST_GeomFromText(?)) != 0", [
+ $this->toWkt($geometry),
+ ]);
+ }
+
+ return $query;
+ }
+
+ public function scopeDistanceValue($query, $geometryColumn, $geometry)
+ {
+ if ($this->isColumnAllowed($geometryColumn)) {
+ $columns = $query->getQuery()->columns;
+
+ if (!$columns) {
+ $query->select('*');
+ }
+
+ $geometryColumn .= $this->getConnection() instanceof PostgresConnection ? '::geometry' : '';
+ $query->selectRaw("ST_Distance({$geometryColumn}, ST_GeomFromText(?)) as distance", [
+ $this->toWkt($geometry),
+ ]);
+ }
+
+ return $query;
+ }
+
+ public function scopeDistanceSphere($query, $geometryColumn, $geometry, $distance)
+ {
+ $distFunc = $this->getConnection() instanceof PostgresConnection ? 'ST_DistanceSphere' : 'ST_Distance_Sphere';
+
+ if ($this->isColumnAllowed($geometryColumn)) {
+ $geometryColumn .= $this->getConnection() instanceof PostgresConnection ? '::geometry' : '';
+ $query->whereRaw("{$distFunc}({$geometryColumn}, ST_GeomFromText(?)) <= ?", [
+ $this->toWkt($geometry),
+ $distance,
+ ]);
+ }
+
+ return $query;
+ }
+
+ public function scopeDistanceSphereExcludingSelf($query, $geometryColumn, $geometry, $distance)
+ {
+ $distFunc = $this->getConnection() instanceof PostgresConnection ? 'ST_DistanceSphere' : 'ST_Distance_Sphere';
+
+ if ($this->isColumnAllowed($geometryColumn)) {
+ $query = $this->scopeDistanceSphere($query, $geometryColumn, $geometry, $distance);
+
+ $geometryColumn .= $this->getConnection() instanceof PostgresConnection ? '::geometry' : '';
+ $query->whereRaw("{$distFunc}({$geometryColumn}, ST_GeomFromText(?)) != 0", [
+ $this->toWkt($geometry),
+ ]);
+ }
+
+ return $query;
+ }
+
+ public function scopeDistanceSphereValue($query, $geometryColumn, $geometry)
+ {
+ $distFunc = $this->getConnection() instanceof PostgresConnection ? 'ST_DistanceSphere' : 'ST_Distance_Sphere';
+
+ if ($this->isColumnAllowed($geometryColumn)) {
+ $columns = $query->getQuery()->columns;
+
+ if (!$columns) {
+ $query->select('*');
+ }
+
+ $geometryColumn .= $this->getConnection() instanceof PostgresConnection ? '::geometry' : '';
+ $query->selectRaw("{$distFunc}({$geometryColumn}, ST_GeomFromText(?)) as distance", [
+ $this->toWkt($geometry),
+ ]);
+ }
+
+ return $query;
+ }
+
+ public function scopeComparison($query, $geometryColumn, $geometry, $relationship)
+ {
+ if ($this->isColumnAllowed($geometryColumn)) {
+ $relationship = ucfirst(strtolower($relationship));
+
+ if (!in_array($relationship, $this->stRelations)) {
+ throw new UnknownSpatialRelationFunction($relationship);
+ }
+
+ $geometryColumn .= $this->getConnection() instanceof PostgresConnection ? '::geometry' : '';
+ $query->whereRaw("ST_{$relationship}(`{$geometryColumn}`, ST_GeomFromText(?))", [
+ $this->toWkt($geometry),
+ ]);
+ }
+
+ return $query;
+ }
+
+ public function scopeWithin($query, $geometryColumn, $polygon)
+ {
+ return $this->scopeComparison($query, $geometryColumn, $polygon, 'within');
+ }
+
+ public function scopeCrosses($query, $geometryColumn, $geometry)
+ {
+ return $this->scopeComparison($query, $geometryColumn, $geometry, 'crosses');
+ }
+
+ public function scopeContains($query, $geometryColumn, $geometry)
+ {
+ return $this->scopeComparison($query, $geometryColumn, $geometry, 'contains');
+ }
+
+ public function scopeDisjoint($query, $geometryColumn, $geometry)
+ {
+ return $this->scopeComparison($query, $geometryColumn, $geometry, 'disjoint');
+ }
+
+ public function scopeEquals($query, $geometryColumn, $geometry)
+ {
+ return $this->scopeComparison($query, $geometryColumn, $geometry, 'equals');
+ }
+
+ public function scopeIntersects($query, $geometryColumn, $geometry)
+ {
+ return $this->scopeComparison($query, $geometryColumn, $geometry, 'intersects');
+ }
+
+ public function scopeOverlaps($query, $geometryColumn, $geometry)
+ {
+ return $this->scopeComparison($query, $geometryColumn, $geometry, 'overlaps');
+ }
+
+ public function scopeDoesTouch($query, $geometryColumn, $geometry)
+ {
+ return $this->scopeComparison($query, $geometryColumn, $geometry, 'touches');
+ }
}
diff --git a/src/Exceptions/UnknownSpatialRelationFunction.php b/src/Exceptions/UnknownSpatialRelationFunction.php
new file mode 100644
index 0000000..6a937f6
--- /dev/null
+++ b/src/Exceptions/UnknownSpatialRelationFunction.php
@@ -0,0 +1,7 @@
+getDoctrineSchemaManager()->getDatabasePlatform();
diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php
index a86bae6..973070b 100755
--- a/src/Schema/Blueprint.php
+++ b/src/Schema/Blueprint.php
@@ -21,9 +21,9 @@ public function geometry($column)
* @param $column
* @return \Illuminate\Support\Fluent
*/
- public function point($column)
+ public function point($column, $srid = null)
{
- return $this->addColumn('point', $column);
+ return $this->addColumn('point', $column, compact('srid'));
}
/**
@@ -101,7 +101,7 @@ public function geometryCollection($column)
*/
public function spatialIndex($columns, $name = null)
{
- return $this->indexCommand('spatial', $columns, $name);
+ return $this->indexCommand('spatialIndex', $columns, $name);
}
/**
@@ -110,8 +110,29 @@ public function spatialIndex($columns, $name = null)
* @param string|array $index
* @return \Illuminate\Support\Fluent
*/
- public function dropSpatial($index)
+ public function dropSpatialIndex($index)
{
- return $this->dropIndexCommand('dropIndex', 'spatial', $index);
+ return $this->dropIndexCommand('dropIndex', 'spatialIndex', $index);
+ }
+
+ /**
+ * Enable postgis on this database.
+ * Will create the extension in the database.
+ *
+ * @return \Illuminate\Support\Fluent
+ */
+ public function enablePostgis()
+ {
+ return $this->addCommand('enablePostgis');
+ }
+
+ /**
+ * Disable postgis on this database.
+ * WIll drop the extension in the database.
+ * @return \Illuminate\Support\Fluent
+ */
+ public function disablePostgis()
+ {
+ return $this->addCommand('disablePostgis');
}
}
diff --git a/src/Schema/Grammars/PostgresGrammar.php b/src/Schema/Grammars/PostgresGrammar.php
index ab58ba5..9b2f1b1 100755
--- a/src/Schema/Grammars/PostgresGrammar.php
+++ b/src/Schema/Grammars/PostgresGrammar.php
@@ -85,42 +85,25 @@ public function typeGeography(Fluent $column)
}
/**
- * Adds a statement to add a geometrycollection geometry column
+ * Adds a statement to add a geometry geometry column
*
- * @param Blueprint $blueprint
- * @param Fluent $command
+ * @param Fluent $column
* @return string
*/
- public function compileGeometrycollection(Blueprint $blueprint, Fluent $command)
+ public function typeGeometry(Fluent $column)
{
- $command->type = 'GEOMETRYCOLLECTION';
-
- return $this->compileGeometry($blueprint, $command);
+ return 'GEOGRAPHY(GEOMETRY, 4326)';
}
/**
- * Adds a statement to add a geometry column
+ * Adds a statement to add a geometrycollection geometry column
*
- * @param Blueprint $blueprint
- * @param Fluent $command
+ * @param Fluent $column
* @return string
*/
- protected function compileGeometry(Blueprint $blueprint, Fluent $command)
+ public function typeGeometrycollection(Fluent $column)
{
-
- $dimensions = $command->dimensions ?: 2;
- $typmod = $command->typmod ? 'true' : 'false';
- $srid = $command->srid ?: 4326;
-
- return sprintf(
- "SELECT AddGeometryColumn('%s', '%s', %d, '%s', %d, %s)",
- $blueprint->getTable(),
- $command->column,
- $srid,
- strtoupper($command->type),
- $dimensions,
- $typmod
- );
+ return 'GEOGRAPHY(GEOMETRYCOLLECTION, 4326)';
}
/**
diff --git a/src/SpatialServiceProvider.php b/src/SpatialServiceProvider.php
index e0465a3..4f764a8 100755
--- a/src/SpatialServiceProvider.php
+++ b/src/SpatialServiceProvider.php
@@ -32,14 +32,14 @@ public function register()
// The connection factory is used to create the actual connection instances on
// the database. We will inject the factory into the manager so that it may
// make the connections while they are actually needed and not of before.
- $this->app->singleton('db.factory', function ($app) {
+ $this->app->singleton('db.factory', function($app) {
return new ConnectionFactory($app);
});
// The database manager is used to resolve various connections, since multiple
// connections might be managed. It also implements the connection resolver
// interface which may be used by other components requiring connections.
- $this->app->singleton('db', function ($app) {
+ $this->app->singleton('db', function($app) {
return new DatabaseManager($app, $app['db.factory']);
});
@@ -53,6 +53,7 @@ public function register()
'multipoint' => MultiPoint::class,
'multilinestring' => MultiLineString::class,
'multipolygon' => MultiPolygon::class,
+ 'geomcollection' => GeometryCollection::class,
'geometrycollection' => GeometryCollection::class,
];
$typeNames = array_keys(Type::getTypesMap());
diff --git a/tests/Integration/Migrations/CreateTables.php b/tests/Integration/Migrations/CreateTables.php
new file mode 100644
index 0000000..42df622
--- /dev/null
+++ b/tests/Integration/Migrations/CreateTables.php
@@ -0,0 +1,45 @@
+increments('id');
+ $table->geometry('geo')->default(null)->nullable();
+ $table->point('location'); // required to be not null in order to add an index
+ $table->lineString('line')->default(null)->nullable();
+ $table->polygon('shape')->default(null)->nullable();
+ $table->multiPoint('multi_locations')->default(null)->nullable();
+ $table->multiLineString('multi_lines')->default(null)->nullable();
+ $table->multiPolygon('multi_shapes')->default(null)->nullable();
+ $table->geometryCollection('multi_geometries')->default(null)->nullable();
+ $table->timestamps();
+ });
+
+ Schema::create('no_spatial_fields', function(Blueprint $table) {
+ $table->increments('id');
+ $table->geometry('geometry')->default(null)->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::drop('no_spatial_fields');
+ Schema::drop('test_geometries');
+ }
+}
diff --git a/tests/Integration/Migrations/UpdateTables.php b/tests/Integration/Migrations/UpdateTables.php
new file mode 100644
index 0000000..1571ed0
--- /dev/null
+++ b/tests/Integration/Migrations/UpdateTables.php
@@ -0,0 +1,62 @@
+point('location')->change();
+
+ // The other field changes are just here to test if change works with them, we're not changing anything
+ $table->geometry('geo')->default(null)->nullable()->change();
+ $table->lineString('line')->default(null)->nullable()->change();
+ $table->polygon('shape')->default(null)->nullable()->change();
+ $table->multiPoint('multi_locations')->default(null)->nullable()->change();
+ $table->multiLineString('multi_lines')->default(null)->nullable()->change();
+ $table->multiPolygon('multi_shapes')->default(null)->nullable()->change();
+ $table->geometryCollection('multi_geometries')->default(null)->nullable()->change();
+
+ });
+
+ if (is_a(\DB::connection(), MysqlConnection::class)) {
+ // MySQL < 5.7.5: table has to be MyISAM
+ \DB::statement('ALTER TABLE test_geometries ENGINE = MyISAM');
+ }
+
+ Schema::table('test_geometries', function(Blueprint $table) {
+ // Add a spatial index on the location field
+ $table->spatialIndex('location');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('test_geometries', function(Blueprint $table) {
+ $table->dropSpatialIndex(['location']); // either an array of column names or the index name
+ });
+
+ if (is_a(\DB::connection(), MysqlConnection::class)) {
+ \DB::statement('ALTER TABLE test_geometries ENGINE = InnoDB');
+ }
+
+ Schema::table('test_geometries', function(Blueprint $table) {
+ $table->point('location')->nullable()->change();
+ });
+ }
+}
diff --git a/tests/Integration/Models/GeometryModel.php b/tests/Integration/Models/GeometryModel.php
new file mode 100644
index 0000000..2995f7c
--- /dev/null
+++ b/tests/Integration/Models/GeometryModel.php
@@ -0,0 +1,34 @@
+register(SpatialServiceProvider::class);
+
+ $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();
+
+ $app['config']->set('database.default', 'mysql');
+ $app['config']->set('database.connections.mysql.host', env('DB_HOST', '127.0.0.1'));
+ $app['config']->set('database.connections.mysql.database', 'spatial_test');
+ $app['config']->set('database.connections.mysql.username', 'root');
+ $app['config']->set('database.connections.mysql.password', '');
+ $app['config']->set('database.connections.mysql.modes', [
+ 'ONLY_FULL_GROUP_BY',
+ 'STRICT_TRANS_TABLES',
+ 'NO_ZERO_IN_DATE',
+ 'NO_ZERO_DATE',
+ 'ERROR_FOR_DIVISION_BY_ZERO',
+ 'NO_ENGINE_SUBSTITUTION',
+ ]);
+
+ return $app;
+ }
+
+ /**
+ * Setup DB before each test.
+ *
+ * @return void
+ */
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->after_fix = $this->isMySQL8AfterFix();
+
+ $this->onMigrations(function($migrationClass) {
+ (new $migrationClass())->up();
+ });
+
+ //\DB::listen(function($sql) {
+ // var_dump($sql);
+ //});
+ }
+
+ public function tearDown()
+ {
+ $this->onMigrations(function($migrationClass) {
+ (new $migrationClass())->down();
+ }, true);
+
+ parent::tearDown();
+ }
+
+ // MySQL 8.0.4 fixed bug #26941370 and bug #88031
+ protected function isMySQL8AfterFix()
+ {
+ $results = DB::select(DB::raw('select version()'));
+ $mysql_version = $results[0]->{'version()'};
+
+ return version_compare($mysql_version, '8.0.4', '>=');
+ }
+
+ protected function assertDatabaseHas($table, array $data, $connection = null)
+ {
+ if (method_exists($this, 'seeInDatabase')) {
+ $this->seeInDatabase($table, $data, $connection);
+ } else {
+ parent::assertDatabaseHas($table, $data, $connection);
+ }
+ }
+
+ protected function assertException($exceptionName)
+ {
+ if (method_exists(parent::class, 'expectException')) {
+ parent::expectException($exceptionName);
+ } else {
+ /** @scrutinizer ignore-deprecated */
+ $this->setExpectedException($exceptionName);
+ }
+ }
+
+ private function onMigrations(\Closure $closure, $reverse_sort = false)
+ {
+ $fileSystem = new Filesystem();
+ $classFinder = new Tools\ClassFinder();
+
+ $migrations = $fileSystem->files(__DIR__ . '/Migrations');
+ $reverse_sort ? rsort($migrations, SORT_STRING) : sort($migrations, SORT_STRING);
+
+ foreach ($migrations as $file) {
+ $fileSystem->requireOnce($file);
+ $migrationClass = $classFinder->findClass($file);
+
+ $closure($migrationClass);
+ }
+ }
+
+ public function testSpatialFieldsNotDefinedException()
+ {
+ $this->assertException(\LaravelSpatial\Exceptions\SpatialFieldsNotDefinedException::class);
+
+ $geo = new NoSpatialFieldsModel();
+ $geo->geometry = new Point([1, 2]);
+ $geo->save();
+
+ NoSpatialFieldsModel::all();
+ }
+
+ public function testInsertPoint()
+ {
+ $geo = new GeometryModel();
+ $geo->location = new Point([1, 2]);
+ $geo->save();
+ $this->assertDatabaseHas($geo->getTable(), ['id' => $geo->id]);
+ }
+
+ public function testInsertLineString()
+ {
+ $geo = new GeometryModel();
+
+ $geo->location = new Point([1, 2]);
+ $geo->line = new LineString([new Point([1, 1]), new Point([2, 2])]);
+ $geo->save();
+ $this->assertDatabaseHas($geo->getTable(), ['id' => $geo->id]);
+ }
+
+ public function testInsertPolygon()
+ {
+ $geo = new GeometryModel();
+
+ $geo->location = new Point([1, 2]);
+ $geo->shape = new Polygon([[[0, 10], [10, 10], [10, 0], [0, 0], [0, 10]]]);
+ $geo->save();
+ $this->assertDatabaseHas($geo->getTable(), ['id' => $geo->id]);
+ }
+
+ public function testInsertMultiPoint()
+ {
+ $geo = new GeometryModel();
+
+ $geo->location = new Point([1, 2]);
+ $geo->multi_locations = new MultiPoint([new Point([1, 1]), new Point([2, 2])]);
+ $geo->save();
+ $this->assertDatabaseHas($geo->getTable(), ['id' => $geo->id]);
+ }
+
+ public function testInsertMultiPolygon()
+ {
+ $geo = new GeometryModel();
+
+ $geo->location = new Point([1, 2]);
+
+ $geo->multi_shapes = new MultiPolygon([
+ new Polygon([[[0, 10], [10, 10], [10, 0], [0, 0], [0, 10]]]),
+ new Polygon([[[0, 0], [0, 5], [5, 5], [5, 0], [0, 0]]]),
+ ]);
+ $geo->save();
+ $this->assertDatabaseHas($geo->getTable(), ['id' => $geo->id]);
+ }
+
+ public function testInsertGeometryCollection()
+ {
+ $geo = new GeometryModel();
+
+ $geo->location = new Point([1, 2]);
+
+ $geo->multi_geometries = new GeometryCollection([
+ new Polygon([[[0, 10], [10, 10], [10, 0], [0, 0], [0, 10]]]),
+ new Polygon([[[0, 0], [0, 5], [5, 5], [5, 0], [0, 0]]]),
+ new Point([0, 0]),
+ ]);
+ $geo->save();
+ $this->assertDatabaseHas($geo->getTable(), ['id' => $geo->id]);
+ }
+
+ public function testUpdate()
+ {
+ $geo = new GeometryModel();
+ $geo->location = new Point([1, 2]);
+ $geo->save();
+
+ $to_update = GeometryModel::all()->first();
+ $to_update->location = new Point([2, 3]);
+ $to_update->save();
+
+ $this->assertDatabaseHas($geo->getTable(), ['id' => $to_update->id]);
+
+ $all = GeometryModel::all();
+ $this->assertCount(1, $all);
+
+ $updated = $all->first();
+ $this->assertInstanceOf(Point::class, $updated->location);
+ $this->assertEquals(2, $updated->location->getCoordinates()[0]);
+ $this->assertEquals(3, $updated->location->getCoordinates()[1]);
+ }
+
+ public function testDistance()
+ {
+ $loc1 = new GeometryModel();
+ $loc1->location = new Point([1, 1]);
+ $loc1->save();
+
+ $loc2 = new GeometryModel();
+ $loc2->location = new Point([2, 2]); // Distance from loc1: 1.4142135623731
+ $loc2->save();
+
+ $loc3 = new GeometryModel();
+ $loc3->location = new Point([3, 3]); // Distance from loc1: 2.8284271247462
+ $loc3->save();
+
+ $a = GeometryModel::distance('location', $loc1->location, 2)->get();
+ $this->assertCount(2, $a);
+ $this->assertTrue($a->contains('location', $loc1->location));
+ $this->assertTrue($a->contains('location', $loc2->location));
+ $this->assertFalse($a->contains('location', $loc3->location));
+
+ // Excluding self
+ $b = GeometryModel::distanceExcludingSelf('location', $loc1->location, 2)->get();
+ $this->assertCount(1, $b);
+ $this->assertFalse($b->contains('location', $loc1->location));
+ $this->assertTrue($b->contains('location', $loc2->location));
+ $this->assertFalse($b->contains('location', $loc3->location));
+
+ $c = GeometryModel::distance('location', $loc1->location, 1)->get();
+ $this->assertCount(1, $c);
+ $this->assertTrue($c->contains('location', $loc1->location));
+ $this->assertFalse($c->contains('location', $loc2->location));
+ $this->assertFalse($c->contains('location', $loc3->location));
+ }
+
+ public function testDistanceSphere()
+ {
+ try {
+ $loc1 = new GeometryModel();
+ $loc1->location = new Point([-73.971732, 40.767864]);
+ $loc1->save();
+
+ $loc2 = new GeometryModel();
+ $loc2->location = new Point([-73.971271, 40.767664]); // Distance from loc1: 44.741406484588
+ $loc2->save();
+
+ $loc3 = new GeometryModel();
+ $loc3->location = new Point([-73.977619, 40.761434]); // Distance from loc1: 870.06424066202
+ $loc3->save();
+
+ $a = GeometryModel::distanceSphere('location', $loc1->location, 200)->get();
+ $this->assertCount(2, $a);
+ $this->assertTrue($a->contains('location', $loc1->location));
+ $this->assertTrue($a->contains('location', $loc2->location));
+ $this->assertFalse($a->contains('location', $loc3->location));
+
+ // Excluding self
+ $b = GeometryModel::distanceSphereExcludingSelf('location', $loc1->location, 200)->get();
+ $this->assertCount(1, $b);
+ $this->assertFalse($b->contains('location', $loc1->location));
+ $this->assertTrue($b->contains('location', $loc2->location));
+ $this->assertFalse($b->contains('location', $loc3->location));
+
+ if ($this->is_postgres || $this->after_fix) {
+ $c = GeometryModel::distanceSphere('location', $loc1->location, 44.741406484236)->get();
+ } else {
+ $c = GeometryModel::distanceSphere('location', $loc1->location, 44.741406484587)->get();
+ }
+ $this->assertCount(1, $c);
+ $this->assertTrue($c->contains('location', $loc1->location));
+ $this->assertFalse($c->contains('location', $loc2->location));
+ $this->assertFalse($c->contains('location', $loc3->location));
+ } catch (\Illuminate\Database\QueryException $e) {
+ if (strpos($e->getMessage(), 'FUNCTION spatial_test.ST_Distance_Sphere does not exist') > -1) {
+ $this->markTestSkipped('Spherical distance tests [distanceSphere*()] not supported on the current DBMS');
+ }
+ throw $e;
+ }
+ }
+
+ public function testDistanceValue()
+ {
+ $loc1 = new GeometryModel();
+ $loc1->location = new Point([1, 1]);
+ $loc1->save();
+
+ $loc2 = new GeometryModel();
+ $loc2->location = new Point([2, 2]); // Distance from loc1: 1.4142135623731
+ $loc2->save();
+
+ $a = GeometryModel::distanceValue('location', $loc1->location)->get();
+ $this->assertCount(2, $a);
+ $this->assertEquals(0, $a[0]->distance);
+ $this->assertEquals(1.4142135623, $a[1]->distance); // PHP floats' 11th+ digits don't matter
+ }
+
+ public function testDistanceSphereValue()
+ {
+ try {
+ $loc1 = new GeometryModel();
+ $loc1->location = new Point([-73.971732, 40.767864]);
+ $loc1->save();
+
+ $loc2 = new GeometryModel();
+ $loc2->location = new Point([-73.971271, 40.767664]); // Distance from loc1: 44.741406484236
+ $loc2->save();
+
+ $a = GeometryModel::distanceSphereValue('location', $loc1->location)->get();
+ $this->assertCount(2, $a);
+ $this->assertEquals(0, $a[0]->distance);
+
+ if ($this->is_postgres) {
+ $this->assertEquals("44.7415664", number_format($a[1]->distance, 7)); // Postgres calculates this differently?
+ } elseif ($this->after_fix) {
+ $this->assertEquals(44.7414064842, $a[1]->distance); // PHP floats' 11th+ digits don't matter
+ } else {
+ $this->assertEquals(44.7414064845, $a[1]->distance); // PHP floats' 11th+ digits don't matter
+ }
+ } catch (\Illuminate\Database\QueryException $e) {
+ if (strpos($e->getMessage(), 'FUNCTION spatial_test.ST_Distance_Sphere does not exist') > -1) {
+ $this->markTestSkipped('Spherical distance tests [distanceSphere*()] not supported on the current DBMS');
+ }
+ throw $e;
+ }
+ }
+
+ //public function testBounding() {
+ // $point = new Point([0, 0]);
+ //
+ // $linestring1 = \GeoJson\Geometry\LineString::fromWkt("LINESTRING(1 1, 2 2)");
+ // $linestring2 = \GeoJson\Geometry\LineString::fromWkt("LINESTRING(20 20, 24 24)");
+ // $linestring3 = \GeoJson\Geometry\LineString::fromWkt("LINESTRING(0 10, 10 10)");
+ //
+ // $geo1 = new GeometryModel();
+ // $geo1->location = $point;
+ // $geo1->line = $linestring1;
+ // $geo1->save();
+ //
+ // $geo2 = new GeometryModel();
+ // $geo2->location = $point;
+ // $geo2->line = $linestring2;
+ // $geo2->save();
+ //
+ // $geo3 = new GeometryModel();
+ // $geo3->location = $point;
+ // $geo3->line = $linestring3;
+ // $geo3->save();
+ //
+ // $polygon = new Polygon([[[0, 10],[10, 10],[10, 0],[0, 0],[0, 10]]]);
+ //
+ // $result = GeometryModel::Bounding($polygon, 'line')->get();
+ // $this->assertCount(2, $result);
+ // $this->assertTrue($result->contains($geo1));
+ // $this->assertFalse($result->contains($geo2));
+ // $this->assertTrue($result->contains($geo3));
+ //
+ //}
+}
diff --git a/tests/Integration/PostgresTest.php b/tests/Integration/PostgresTest.php
new file mode 100644
index 0000000..1f7921a
--- /dev/null
+++ b/tests/Integration/PostgresTest.php
@@ -0,0 +1,29 @@
+set('database.default', 'pgsql');
+ $app['config']->set('database.connections.pgsql.host', env('DB_HOST', '127.0.0.1'));
+ $app['config']->set('database.connections.pgsql.database', 'spatial_test');
+ $app['config']->set('database.connections.pgsql.username', 'postgres');
+ $app['config']->set('database.connections.pgsql.password', '');
+
+ return $app;
+ }
+
+ protected function isMySQL8AfterFix()
+ {
+ return false;
+ }
+}
diff --git a/tests/Integration/Tools/ClassFinder.php b/tests/Integration/Tools/ClassFinder.php
new file mode 100644
index 0000000..5bc7caf
--- /dev/null
+++ b/tests/Integration/Tools/ClassFinder.php
@@ -0,0 +1,154 @@
+in($directory)->name('*.php') as $file) {
+ $classes[] = $this->findClass($file->getRealPath());
+ }
+
+ return array_filter($classes);
+ }
+
+ /**
+ * Extract the class name from the file at the given path.
+ *
+ * @param string $path
+ *
+ * @return string|null
+ */
+ public function findClass($path)
+ {
+ $namespace = null;
+
+ $tokens = token_get_all(file_get_contents($path));
+
+ foreach ($tokens as $key => $token) {
+ if ($this->tokenIsNamespace($token)) {
+ $namespace = $this->getNamespace($key + 2, $tokens);
+ } elseif ($this->tokenIsClassOrInterface($token)) {
+ return ltrim($namespace . '\\' . $this->getClass($key + 2, $tokens), '\\');
+ }
+ }
+ }
+
+ /**
+ * Find the namespace in the tokens starting at a given key.
+ *
+ * @param int $key
+ * @param array $tokens
+ *
+ * @return string
+ */
+ protected function getNamespace($key, array $tokens)
+ {
+ $namespace = null;
+
+ $tokenCount = count($tokens);
+
+ for ($i = $key; $i < $tokenCount; $i++) {
+ if ($this->isPartOfNamespace($tokens[$i])) {
+ $namespace .= $tokens[$i][1];
+ } elseif ($tokens[$i] == ';') {
+ return $namespace;
+ }
+ }
+ }
+
+ /**
+ * Find the class in the tokens starting at a given key.
+ *
+ * @param int $key
+ * @param array $tokens
+ *
+ * @return string
+ */
+ protected function getClass($key, array $tokens)
+ {
+ $class = null;
+
+ $tokenCount = count($tokens);
+
+ for ($i = $key; $i < $tokenCount; $i++) {
+ if ($this->isPartOfClass($tokens[$i])) {
+ $class .= $tokens[$i][1];
+ } elseif ($this->isWhitespace($tokens[$i])) {
+ return $class;
+ }
+ }
+ }
+
+ /**
+ * Determine if the given token is a namespace keyword.
+ *
+ * @param array|string $token
+ *
+ * @return bool
+ */
+ protected function tokenIsNamespace($token)
+ {
+ return is_array($token) && $token[0] == T_NAMESPACE;
+ }
+
+ /**
+ * Determine if the given token is a class or interface keyword.
+ *
+ * @param array|string $token
+ *
+ * @return bool
+ */
+ protected function tokenIsClassOrInterface($token)
+ {
+ return is_array($token) && ($token[0] == T_CLASS || $token[0] == T_INTERFACE);
+ }
+
+ /**
+ * Determine if the given token is part of the namespace.
+ *
+ * @param array|string $token
+ *
+ * @return bool
+ */
+ protected function isPartOfNamespace($token)
+ {
+ return is_array($token) && ($token[0] == T_STRING || $token[0] == T_NS_SEPARATOR);
+ }
+
+ /**
+ * Determine if the given token is part of the class.
+ *
+ * @param array|string $token
+ *
+ * @return bool
+ */
+ protected function isPartOfClass($token)
+ {
+ return is_array($token) && $token[0] == T_STRING;
+ }
+
+ /**
+ * Determine if the given token is whitespace.
+ *
+ * @param array|string $token
+ *
+ * @return bool
+ */
+ protected function isWhitespace($token)
+ {
+ return is_array($token) && $token[0] == T_WHITESPACE;
+ }
+}
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..7663c04
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,73 @@
+mySrv=mysql
+
+oV=5.7
+oDB_DIR=$(shell pwd)/_odb-$(oV)
+
+mV=10.3
+mDB_DIR=$(shell pwd)/_mdb-$(mV)
+
+pV=10
+pDB_DIR=$(shell pwd)/_pdb-$(pV)
+
+start_db_mysql:
+ @echo Starting MySQL $(oV)
+ docker run --rm -d --name spatial-mysql \
+ -p 3306:3306 \
+ -v $(oDB_DIR):/var/lib/mysql \
+ --health-cmd='mysqladmin ping --silent && mysql -e "use spatial_test;"' \
+ -e MYSQL_DATABASE=spatial_test \
+ -e MYSQL_ALLOW_EMPTY_PASSWORD=yes \
+ mysql:$(oV) --character-set-server=utf8 --collation-server=utf8_general_ci --default-authentication-plugin=mysql_native_password
+
+start_db_maria:
+ @echo Starting MariaDB $(mV)
+ docker run --rm -d --name spatial-mysql \
+ -p 3306:3306 \
+ -v $(mDB_DIR):/var/lib/mysql \
+ --health-cmd='mysqladmin ping --silent && mysql -e "use spatial_test;"' \
+ -e MYSQL_DATABASE=spatial_test \
+ -e MYSQL_ALLOW_EMPTY_PASSWORD=yes \
+ mariadb:$(mV) --character-set-server=utf8 --collation-server=utf8_general_ci
+
+start_db_postgres:
+ @echo Starting Postgres $(pV)
+ docker run --rm -d --name spatial-postgres \
+ -p 5432:5432 \
+ -v $(pDB_DIR):/var/lib/postgresql/data \
+ --health-cmd='if [[ $(pV) -ge 9.3 ]]; then pg_isready -U postgres; else psql -U postgres -c "SELECT 1"; fi' \
+ -e POSTGRES_DB=spatial_test \
+ -e POSTGRES_PASSWORD="" \
+ mdillon/postgis:$(pV)
+
+rm_db_mysql:
+ docker stop spatial-mysql || true
+ sudo rm -Rf $(oDB_DIR)
+
+rm_db_maria:
+ docker stop spatial-mysql || true
+ sudo rm -Rf $(mDB_DIR)
+
+rm_db_postgres:
+ docker stop spatial-postgres || true
+ sudo rm -Rf $(pDB_DIR)
+
+refresh_db_mysql: rm_db_mysql start_db_mysql
+
+refresh_db_maria: rm_db_maria start_db_maria
+
+refresh_db_postgres: rm_db_postgres start_db_postgres
+
+get_ip_mysql:
+ @docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' spatial-mysql
+
+get_ip_postgres:
+ @docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' spatial-postgres
+
+run_tests: start_db_${mySrv} start_db_postgres __do_test rm_db_${mySrv} rm_db_postgres
+
+__do_test:
+ @echo -n "\nWaiting for servers to fully start"
+ @./wait_container spatial-mysql
+ @./wait_container spatial-postgres
+ @echo " Ready! Starting tests.\n"
+ @cd .. && ./vendor/bin/phpunit || true
diff --git a/tests/Unit/BaseTestCase.php b/tests/Unit/BaseTestCase.php
new file mode 100644
index 0000000..769b4f8
--- /dev/null
+++ b/tests/Unit/BaseTestCase.php
@@ -0,0 +1,19 @@
+setExpectedException($exceptionName);
+ }
+ }
+}
diff --git a/tests/Unit/Connectors/ConnectionFactoryTest.php b/tests/Unit/Connectors/ConnectionFactoryTest.php
new file mode 100644
index 0000000..cbcfbb2
--- /dev/null
+++ b/tests/Unit/Connectors/ConnectionFactoryTest.php
@@ -0,0 +1,43 @@
+makePartial();
+ $factory->shouldAllowMockingProtectedMethods();
+ $conn = $factory->createConnection('mysql', $pdo, 'database');
+
+ $this->assertInstanceOf(MysqlConnection::class, $conn);
+ }
+
+ public function testPostgresMakeCallsCreateConnection()
+ {
+ $pdo = new PDOStub();
+
+ $factory = Mockery::mock(ConnectionFactory::class, [new Container()])->makePartial();
+ $factory->shouldAllowMockingProtectedMethods();
+ $conn = $factory->createConnection('pgsql', $pdo, 'database');
+
+ $this->assertInstanceOf(PostgresConnection::class, $conn);
+ }
+
+ public function testCreateConnectionDifferentDriver()
+ {
+ $pdo = new PDOStub();
+
+ $factory = Mockery::mock(ConnectionFactory::class, [new Container()])->makePartial();
+ $factory->shouldAllowMockingProtectedMethods();
+ $conn = $factory->createConnection('sqlite', $pdo, 'database');
+
+ $this->assertInstanceOf(\Illuminate\Database\SQLiteConnection::class, $conn);
+ }
+}
diff --git a/tests/Unit/Eloquent/BuilderTest.php b/tests/Unit/Eloquent/BuilderTest.php
new file mode 100644
index 0000000..3ff4740
--- /dev/null
+++ b/tests/Unit/Eloquent/BuilderTest.php
@@ -0,0 +1,102 @@
+makePartial();
+ $grammar = Mockery::mock(MySqlGrammar::class)->makePartial();
+ $this->queryBuilder = Mockery::mock(QueryBuilder::class, [$connection, $grammar]);
+
+ $this->queryBuilder
+ ->shouldReceive('from')
+ ->once()
+ ->andReturn($this->queryBuilder);
+
+ $this->queryBuilder
+ ->shouldReceive('take')
+ ->with(1)
+ ->andReturn($this->queryBuilder);
+
+ $this->queryBuilder
+ ->shouldReceive('get')
+ ->andReturn([]);
+
+ $this->builder = new Builder($this->queryBuilder);
+ $this->builder->setModel(new TestBuilderModel());
+ }
+
+ public function testUpdatePoint()
+ {
+ $point = new Point([1, 2]);
+
+ $this->queryBuilder
+ ->shouldReceive('raw')
+ ->with("ST_GeomFromText('POINT (1 2)')")
+ ->andReturn(new Expression("ST_GeomFromText('POINT (1 2)')"));
+
+ $this->queryBuilder
+ ->shouldReceive('update')
+ ->andReturn(1);
+
+ $this->builder->update(['point' => $point]);
+ }
+
+ public function testUpdateLinestring()
+ {
+ $linestring = new LineString([new Point([0, 0]), new Point([1, 1]), new Point([2, 2])]);
+
+ $this->queryBuilder
+ ->shouldReceive('raw')
+ ->with("ST_GeomFromText('LINESTRING (0 0, 1 1, 2 2)')")
+ ->andReturn(new Expression("ST_GeomFromText('LINESTRING (0 0, 1 1, 2 2)')"));
+
+ $this->queryBuilder
+ ->shouldReceive('update')
+ ->andReturn(1);
+
+ $this->builder->update(['linestring' => $linestring]);
+ }
+
+ public function testUpdatePolygon()
+ {
+ $polygon = new Polygon([[new Point([0, 0]), new Point([0, 1]), new Point([1, 1]), new Point([0, 0])]]);
+
+ $this->queryBuilder
+ ->shouldReceive('raw')
+ ->with("ST_GeomFromText('POLYGON ((0 0, 0 1, 1 1, 0 0))')")
+ ->andReturn(new Expression("ST_GeomFromText('POLYGON ((0 0, 0 1, 1 1, 0 0))')"));
+
+ $this->queryBuilder
+ ->shouldReceive('update')
+ ->andReturn(1);
+
+ $this->builder->update(['polygon' => $polygon]);
+ }
+}
+
+class TestBuilderModel extends Model
+{
+ use SpatialTrait;
+
+ public $timestamps = false;
+ protected $spatialFields = ['point', 'linestring', 'polygon'];
+}
diff --git a/tests/Unit/Eloquent/SpatialTraitTest.php b/tests/Unit/Eloquent/SpatialTraitTest.php
new file mode 100644
index 0000000..b6888af
--- /dev/null
+++ b/tests/Unit/Eloquent/SpatialTraitTest.php
@@ -0,0 +1,577 @@
+model = new TestModel();
+ $this->queries = &$this->model->getConnection()->getPdo()->queries;
+ }
+
+ public function tearDown()
+ {
+ $this->model->getConnection()->getPdo()->resetQueries();
+ }
+
+ public function testInsertUpdatePointHasCorrectSql()
+ {
+ $this->assertFalse($this->model->exists);
+
+ $this->model->point = new Point([1, 2]);
+ $this->model->save();
+
+ $this->assertStringStartsWith('insert', $this->queries[0]);
+ $this->assertContains("insert into `test_models` (`point`) values (ST_GeomFromText('POINT (1 2)'))", $this->queries[0]);
+ // TODO: assert bindings in query
+ $this->assertTrue($this->model->exists);
+
+ $this->model->point = new Point([1, 2]);
+ $this->model->save();
+
+ $this->assertStringStartsWith('update', $this->queries[1]);
+ $this->assertContains("update `test_models` set `point` = ST_GeomFromText('POINT (1 2)') where `id` = ?", $this->queries[1]);
+ // TODO: assert bindings in query
+ }
+
+ public function testInsertUpdateLineStringHasCorrectSql()
+ {
+ $point1 = new Point([1, 2]);
+ $point2 = new Point([2, 3]);
+
+ $this->assertFalse($this->model->exists);
+
+ $this->model->linestring = new LineString([$point1, $point2]);
+ $this->model->save();
+
+ $this->assertStringStartsWith('insert', $this->queries[0]);
+ $this->assertContains("insert into `test_models` (`linestring`) values (ST_GeomFromText('LINESTRING (1 2, 2 3)'))", $this->queries[0]);
+ // TODO: assert bindings in query
+ $this->assertTrue($this->model->exists);
+
+ $this->model->linestring = new LineString([$point1, $point2]);
+ $this->model->save();
+
+ $this->assertStringStartsWith('update', $this->queries[1]);
+ $this->assertContains("update `test_models` set `linestring` = ST_GeomFromText('LINESTRING (1 2, 2 3)') where `id` = ?", $this->queries[1]);
+ // TODO: assert bindings in query
+ }
+
+ public function testInsertUpdatePolygonHasCorrectSql()
+ {
+ $point1 = new Point([1, 2]);
+ $point2 = new Point([2, 3]);
+ $point3 = new Point([3, 2]);
+ $point4 = new Point([2, 1]);
+
+ $this->assertFalse($this->model->exists);
+
+ $this->model->polygon = new Polygon([[$point1, $point2, $point3, $point4, $point1]]);
+ $this->model->save();
+
+ $this->assertStringStartsWith('insert', $this->queries[0]);
+ $this->assertContains("insert into `test_models` (`polygon`) values (ST_GeomFromText('POLYGON ((1 2, 2 3, 3 2, 2 1, 1 2))'))", $this->queries[0]);
+ // TODO: assert bindings in query
+ $this->assertTrue($this->model->exists);
+
+ $this->model->polygon = new Polygon([[$point1, $point2, $point3, $point4, $point1]]);
+ $this->model->save();
+ $this->assertStringStartsWith('update', $this->queries[1]);
+ $this->assertContains("update `test_models` set `polygon` = ST_GeomFromText('POLYGON ((1 2, 2 3, 3 2, 2 1, 1 2))') where `id` = ?", $this->queries[1]);
+ // TODO: assert bindings in query
+ }
+
+ public function testInsertUpdateMultiPointHasCorrectSql()
+ {
+ $point1 = new Point([1, 2]);
+ $point2 = new Point([2, 3]);
+
+ $this->assertFalse($this->model->exists);
+
+ $this->model->multipoint = new MultiPoint([$point1, $point2]);
+ $this->model->save();
+
+ $this->assertStringStartsWith('insert', $this->queries[0]);
+ $this->assertContains("insert into `test_models` (`multipoint`) values (ST_GeomFromText('MULTIPOINT ((1 2), (2 3))'))", $this->queries[0]);
+ // TODO: assert bindings in query
+ $this->assertTrue($this->model->exists);
+
+ $this->model->multipoint = new MultiPoint([$point1, $point2]);
+ $this->model->save();
+
+ $this->assertStringStartsWith('update', $this->queries[1]);
+ $this->assertContains("update `test_models` set `multipoint` = ST_GeomFromText('MULTIPOINT ((1 2), (2 3))') where `id` = ?", $this->queries[1]);
+ // TODO: assert bindings in query
+ }
+
+ public function testInsertUpdateMultiLineStringHasCorrectSql()
+ {
+ $point1 = new Point([1, 2]);
+ $point2 = new Point([2, 3]);
+ $linestring1 = new LineString([$point1, $point2]);
+ $point3 = new Point([3, 2]);
+ $point4 = new Point([2, 1]);
+ $linestring2 = new LineString([$point3, $point4]);
+
+ $this->assertFalse($this->model->exists);
+
+ $this->model->multilinestring = new MultiLineString([$linestring1, $linestring2]);
+ $this->model->save();
+
+ $this->assertStringStartsWith('insert', $this->queries[0]);
+ $this->assertContains("insert into `test_models` (`multilinestring`) values (ST_GeomFromText('MULTILINESTRING ((1 2, 2 3), (3 2, 2 1))'))", $this->queries[0]);
+ // TODO: assert bindings in query
+ $this->assertTrue($this->model->exists);
+
+ $this->model->multilinestring = new MultiLineString([$linestring1, $linestring2]);
+ $this->model->save();
+ $this->assertStringStartsWith('update', $this->queries[1]);
+ $this->assertContains("update `test_models` set `multilinestring` = ST_GeomFromText('MULTILINESTRING ((1 2, 2 3), (3 2, 2 1))') where `id` = ?", $this->queries[1]);
+ // TODO: assert bindings in query
+ }
+
+ public function testInsertUpdateMultiPolygonHasCorrectSql()
+ {
+ $point1 = new Point([1, 2]);
+ $point2 = new Point([2, 3]);
+ $point3 = new Point([3, 2]);
+ $point4 = new Point([2, 1]);
+ $point5 = new Point([4, 5]);
+ $point6 = new Point([5, 6]);
+ $point7 = new Point([6, 5]);
+ $point8 = new Point([5, 4]);
+
+ $polygon1 = new Polygon([[$point1, $point2, $point3, $point4, $point1]]);
+ $polygon2 = new Polygon([[$point5, $point6, $point7, $point8, $point5]]);
+
+ $this->assertFalse($this->model->exists);
+
+ $this->model->multipolygon = new MultiPolygon([$polygon1, $polygon2]);
+ $this->model->save();
+
+ $this->assertStringStartsWith('insert', $this->queries[0]);
+ $this->assertContains("insert into `test_models` (`multipolygon`) values (ST_GeomFromText('MULTIPOLYGON (((1 2, 2 3, 3 2, 2 1, 1 2)), ((4 5, 5 6, 6 5, 5 4, 4 5)))'))", $this->queries[0]);
+ // TODO: assert bindings in query
+ $this->assertTrue($this->model->exists);
+
+ $this->model->multipolygon = new MultiPolygon([$polygon1, $polygon2]);
+ $this->model->save();
+ $this->assertStringStartsWith('update', $this->queries[1]);
+ $this->assertContains("update `test_models` set `multipolygon` = ST_GeomFromText('MULTIPOLYGON (((1 2, 2 3, 3 2, 2 1, 1 2)), ((4 5, 5 6, 6 5, 5 4, 4 5)))') where `id` = ?", $this->queries[1]);
+ // TODO: assert bindings in query
+ }
+
+ public function testInsertUpdateGeometryCollectionHasCorrectSql()
+ {
+ $point1 = new Point([1, 2]);
+ $linestring1 = new LineString([[2, 3], [3, 3]]);
+
+ $this->assertFalse($this->model->exists);
+
+ $this->model->geometrycollection = new GeometryCollection([$point1, $linestring1]);
+ $this->model->save();
+
+ $this->assertStringStartsWith('insert', $this->queries[0]);
+ $this->assertContains("insert into `test_models` (`geometrycollection`) values (ST_GeomFromText('GEOMETRYCOLLECTION (POINT (1 2), LINESTRING (2 3, 3 3))'))", $this->queries[0]);
+ // TODO: assert bindings in query
+ $this->assertTrue($this->model->exists);
+
+ $this->model->geometrycollection = new GeometryCollection([$point1, $linestring1]);
+ $this->model->save();
+ $this->assertStringStartsWith('update', $this->queries[1]);
+ $this->assertContains("update `test_models` set `geometrycollection` = ST_GeomFromText('GEOMETRYCOLLECTION (POINT (1 2), LINESTRING (2 3, 3 3))') where `id` = ?", $this->queries[1]);
+ // TODO: assert bindings in query
+ }
+
+ public function testSettingRawAttributes()
+ {
+ $attributes['point'] = pack('H*', '0101000000000000000000f03f0000000000000040');
+
+ $this->model->setRawAttributes($attributes);
+ $this->assertInstanceOf(Point::class, ($this->model->point));
+ }
+
+ public function testSpatialFieldsNotDefinedException()
+ {
+ $model = new TestNoSpatialModel();
+ $this->assertException(SpatialFieldsNotDefinedException::class);
+ $model->getSpatialFields();
+ }
+
+ public function testScopeDistance()
+ {
+ $point = new Point([1, 2]);
+ $query = TestModel::distance('point', $point, 10);
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertEquals('ST_Distance(point, ST_GeomFromText(?)) <= ?', $q->wheres[0]['sql']);
+ $this->assertEquals('POINT (1 2)', $bindings[0]);
+ $this->assertEquals(10, $bindings[1]);
+ }
+
+ public function testScopeDistanceExcludingSelf()
+ {
+ $point = new Point([1, 2]);
+ $query = TestModel::distanceExcludingSelf('point', $point, 10);
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertEquals('ST_Distance(point, ST_GeomFromText(?)) <= ?', $q->wheres[0]['sql']);
+ $this->assertEquals('ST_Distance(point, ST_GeomFromText(?)) != 0', $q->wheres[1]['sql']);
+ $this->assertEquals('POINT (1 2)', $bindings[0]);
+ $this->assertEquals(10, $bindings[1]);
+ $this->assertEquals('POINT (1 2)', $bindings[2]);
+ }
+
+ public function testScopeDistanceSphere()
+ {
+ $point = new Point([1, 2]);
+ $query = TestModel::distanceSphere('point', $point, 10);
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertEquals('ST_Distance_Sphere(point, ST_GeomFromText(?)) <= ?', $q->wheres[0]['sql']);
+ $this->assertEquals('POINT (1 2)', $bindings[0]);
+ $this->assertEquals(10, $bindings[1]);
+ }
+
+ public function testScopeDistanceSphereExcludingSelf()
+ {
+ $point = new Point([1, 2]);
+ $query = TestModel::distanceSphereExcludingSelf('point', $point, 10);
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertEquals('ST_Distance_Sphere(point, ST_GeomFromText(?)) <= ?', $q->wheres[0]['sql']);
+ $this->assertEquals('ST_Distance_Sphere(point, ST_GeomFromText(?)) != 0', $q->wheres[1]['sql']);
+ $this->assertEquals('POINT (1 2)', $bindings[0]);
+ $this->assertEquals(10, $bindings[1]);
+ $this->assertEquals('POINT (1 2)', $bindings[2]);
+ }
+
+ public function testScopeDistanceValue()
+ {
+ $point = new Point([1, 2]);
+ $query = TestModel::distanceValue('point', $point);
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->columns);
+ $bindings = $q->getRawBindings()['select'];
+ $this->assertNotEmpty($bindings);
+ $this->assertEquals('*', $q->columns[0]);
+ $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]);
+ $this->assertEquals('ST_Distance(point, ST_GeomFromText(?)) as distance', $q->columns[1]->getValue());
+ $this->assertEquals('POINT (1 2)', $bindings[0]);
+ }
+
+ public function testScopeDistanceValueWithSelect()
+ {
+ $point = new Point([1, 2]);
+ $query = TestModel::select('some_column')->distanceValue('point', $point);
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->columns);
+ $bindings = $q->getRawBindings()['select'];
+ $this->assertNotEmpty($bindings);
+ $this->assertEquals('some_column', $q->columns[0]);
+ $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]);
+ $this->assertEquals('ST_Distance(point, ST_GeomFromText(?)) as distance', $q->columns[1]->getValue());
+ $this->assertEquals('POINT (1 2)', $bindings[0]);
+ }
+
+ public function testScopeDistanceSphereValue()
+ {
+ $point = new Point([1, 2]);
+ $query = TestModel::distanceSphereValue('point', $point);
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->columns);
+ $bindings = $q->getRawBindings()['select'];
+ $this->assertNotEmpty($bindings);
+ $this->assertEquals('*', $q->columns[0]);
+ $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]);
+ $this->assertEquals('ST_Distance_Sphere(point, ST_GeomFromText(?)) as distance', $q->columns[1]->getValue());
+ $this->assertEquals('POINT (1 2)', $bindings[0]);
+ }
+
+ public function testScopeDistanceSphereValueWithSelect()
+ {
+ $point = new Point([1, 2]);
+ $query = TestModel::select('some_column')->distanceSphereValue('point', $point);
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->columns);
+ $bindings = $q->getRawBindings()['select'];
+ $this->assertNotEmpty($bindings);
+ $this->assertEquals('some_column', $q->columns[0]);
+ $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]);
+ $this->assertEquals('ST_Distance_Sphere(point, ST_GeomFromText(?)) as distance', $q->columns[1]->getValue());
+ $this->assertEquals('POINT (1 2)', $bindings[0]);
+ }
+
+ private function buildTestPolygon()
+ {
+ $point1 = new Point([1, 1]);
+ $point2 = new Point([1, 2]);
+ $point3 = new Point([1, 2]);
+ $point4 = new Point([2, 2]);
+ $point5 = new Point([2, 2]);
+ $point6 = new Point([1, 1]);
+
+ return new Polygon([[$point1, $point2, $point3, $point4, $point5, $point6]]);
+ }
+
+ public function testScopeComparison()
+ {
+ $query = TestModel::comparison('point', $this->buildTestPolygon(), 'within');
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertContains('ST_Within(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']);
+ $this->assertEquals('POLYGON ((1 1, 1 2, 1 2, 2 2, 2 2, 1 1))', $bindings[0]);
+ }
+
+ public function testScopeWithin()
+ {
+ $query = TestModel::within('point', $this->buildTestPolygon());
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertContains('ST_Within(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']);
+ $this->assertEquals('POLYGON ((1 1, 1 2, 1 2, 2 2, 2 2, 1 1))', $bindings[0]);
+ }
+
+ public function testScopeCrosses()
+ {
+ $query = TestModel::crosses('point', $this->buildTestPolygon());
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertContains('ST_Crosses(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']);
+ $this->assertEquals('POLYGON ((1 1, 1 2, 1 2, 2 2, 2 2, 1 1))', $bindings[0]);
+ }
+
+ public function testScopeContains()
+ {
+ $query = TestModel::contains('point', $this->buildTestPolygon());
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertContains('ST_Contains(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']);
+ $this->assertEquals('POLYGON ((1 1, 1 2, 1 2, 2 2, 2 2, 1 1))', $bindings[0]);
+ }
+
+ public function testScopeDisjoint()
+ {
+ $query = TestModel::disjoint('point', $this->buildTestPolygon());
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertContains('ST_Disjoint(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']);
+ $this->assertEquals('POLYGON ((1 1, 1 2, 1 2, 2 2, 2 2, 1 1))', $bindings[0]);
+ }
+
+ public function testScopeEquals()
+ {
+ $query = TestModel::equals('point', $this->buildTestPolygon());
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertContains('ST_Equals(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']);
+ $this->assertEquals('POLYGON ((1 1, 1 2, 1 2, 2 2, 2 2, 1 1))', $bindings[0]);
+ }
+
+ public function testScopeIntersects()
+ {
+ $query = TestModel::intersects('point', $this->buildTestPolygon());
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertContains('ST_Intersects(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']);
+ $this->assertEquals('POLYGON ((1 1, 1 2, 1 2, 2 2, 2 2, 1 1))', $bindings[0]);
+ }
+
+ public function testScopeOverlaps()
+ {
+ $query = TestModel::overlaps('point', $this->buildTestPolygon());
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertContains('ST_Overlaps(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']);
+ $this->assertEquals('POLYGON ((1 1, 1 2, 1 2, 2 2, 2 2, 1 1))', $bindings[0]);
+ }
+
+ public function testScopeDoesTouch()
+ {
+ $query = TestModel::doesTouch('point', $this->buildTestPolygon());
+
+ $this->assertInstanceOf(Builder::class, $query);
+ $q = $query->getQuery();
+ $this->assertNotEmpty($q->wheres);
+ $bindings = $q->getRawBindings()['where'];
+ $this->assertNotEmpty($bindings);
+ $this->assertContains('ST_Touches(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']);
+ $this->assertEquals('POLYGON ((1 1, 1 2, 1 2, 2 2, 2 2, 1 1))', $bindings[0]);
+ }
+}
+
+/**
+ * Test Model
+ *
+ * @property $point
+ * @property $linestring
+ * @property $polygon
+ * @property $multipoint
+ * @property $multilinestring
+ * @property $multipolygon
+ * @property $geometrycollection
+ */
+class TestModel extends Model
+{
+ use SpatialTrait;
+
+ protected $spatialFields = [
+ 'point',
+ 'linestring',
+ 'polygon',
+ 'multipoint',
+ 'multilinestring',
+ 'multipolygon',
+ 'geometrycollection',
+ ];
+
+ public $timestamps = false;
+
+ public static $pdo;
+
+ public static function resolveConnection($connection = null)
+ {
+ if (is_null(static::$pdo)) {
+ static::$pdo = Mockery::mock('TestPDO')->makePartial();
+ }
+
+ return new MysqlConnection(static::$pdo);
+ }
+
+ public function testrelatedmodels()
+ {
+ return $this->hasMany(TestRelatedModel::class);
+ }
+
+ public function testrelatedmodels2()
+ {
+ return $this->belongsToMany(TestRelatedModel::class);
+ }
+}
+
+class TestRelatedModel extends TestModel
+{
+ public function testmodel()
+ {
+ return $this->belongsTo(TestModel::class);
+ }
+
+ public function testmodels()
+ {
+ return $this->belongsToMany(TestModel::class);
+ }
+}
+
+class TestNoSpatialModel extends Model
+{
+ use SpatialTrait;
+}
+
+class TestPDO extends PDO
+{
+ public $queries = [];
+
+ public $counter = 1;
+
+ public function prepare($statement, $driver_options = [])
+ {
+ $this->queries[] = $statement;
+
+ $stmt = Mockery::mock('PDOStatement');
+ $stmt->shouldReceive('bindValue')->zeroOrMoreTimes();
+ $stmt->shouldReceive('execute');
+ $stmt->shouldReceive('fetchAll')->andReturn([['id' => 1, 'point' => 'POINT(1 2)']]);
+ $stmt->shouldReceive('rowCount')->andReturn(1);
+
+ return $stmt;
+ }
+
+ public function lastInsertId($name = null)
+ {
+ return $this->counter++;
+ }
+
+ public function resetQueries()
+ {
+ $this->queries = [];
+ }
+}
diff --git a/tests/Unit/MysqlConnectionTest.php b/tests/Unit/MysqlConnectionTest.php
new file mode 100644
index 0000000..2ca3a39
--- /dev/null
+++ b/tests/Unit/MysqlConnectionTest.php
@@ -0,0 +1,23 @@
+ 'mysql', 'prefix' => 'prefix', 'database' => 'database', 'name' => 'foo'];
+ $this->mysqlConnection = new MysqlConnection(new PDOStub(), 'database', 'prefix', $mysqlConfig);
+ }
+
+ public function testGetSchemaBuilder()
+ {
+ $builder = $this->mysqlConnection->getSchemaBuilder();
+
+ $this->assertInstanceOf(Builder::class, $builder);
+ }
+}
diff --git a/tests/Unit/PostgresConnectionTest.php b/tests/Unit/PostgresConnectionTest.php
new file mode 100644
index 0000000..8a1e81e
--- /dev/null
+++ b/tests/Unit/PostgresConnectionTest.php
@@ -0,0 +1,23 @@
+ 'pgsql', 'prefix' => 'prefix', 'database' => 'database', 'name' => 'foo'];
+ $this->postgresConnection = new PostgresConnection(new PDOStub(), 'database', 'prefix', $pgConfig);
+ }
+
+ public function testGetSchemaBuilder()
+ {
+ $builder = $this->postgresConnection->getSchemaBuilder();
+
+ $this->assertInstanceOf(Builder::class, $builder);
+ }
+}
diff --git a/tests/Unit/Schema/BlueprintTest.php b/tests/Unit/Schema/BlueprintTest.php
new file mode 100644
index 0000000..18c9844
--- /dev/null
+++ b/tests/Unit/Schema/BlueprintTest.php
@@ -0,0 +1,120 @@
+blueprint = Mockery::mock(Blueprint::class)
+ ->makePartial()->shouldAllowMockingProtectedMethods();
+ }
+
+ public function testGeometry()
+ {
+ $this->blueprint
+ ->shouldReceive('addColumn')
+ ->with('geometry', 'col')
+ ->once();
+
+ $this->blueprint->geometry('col');
+ }
+
+ public function testPoint()
+ {
+ $this->blueprint
+ ->shouldReceive('addColumn')
+ ->with('point', 'col', ['srid' => null])
+ ->once();
+
+ $this->blueprint->point('col');
+ }
+
+ public function testLinestring()
+ {
+ $this->blueprint
+ ->shouldReceive('addColumn')
+ ->with('linestring', 'col')
+ ->once();
+
+ $this->blueprint->linestring('col');
+ }
+
+ public function testPolygon()
+ {
+ $this->blueprint
+ ->shouldReceive('addColumn')
+ ->with('polygon', 'col')
+ ->once();
+
+ $this->blueprint->polygon('col');
+ }
+
+ public function testMultiPoint()
+ {
+ $this->blueprint
+ ->shouldReceive('addColumn')
+ ->with('multipoint', 'col')
+ ->once();
+
+ $this->blueprint->multipoint('col');
+ }
+
+ public function testMultiLineString()
+ {
+ $this->blueprint
+ ->shouldReceive('addColumn')
+ ->with('multilinestring', 'col')
+ ->once();
+
+ $this->blueprint->multilinestring('col');
+ }
+
+ public function testMulltiPolygon()
+ {
+ $this->blueprint
+ ->shouldReceive('addColumn')
+ ->with('multipolygon', 'col')
+ ->once();
+
+ $this->blueprint->multipolygon('col');
+ }
+
+ public function testGeometryCollection()
+ {
+ $this->blueprint
+ ->shouldReceive('addColumn')
+ ->with('geometrycollection', 'col')
+ ->once();
+
+ $this->blueprint->geometrycollection('col');
+ }
+
+ public function testEnablePostgis()
+ {
+ $this->blueprint
+ ->shouldReceive('addCommand')
+ ->with('enablePostgis')
+ ->once();
+
+ $this->blueprint->enablePostgis();
+ }
+
+ public function testDisablePostgis()
+ {
+ $this->blueprint
+ ->shouldReceive('addCommand')
+ ->with('disablePostgis')
+ ->once();
+
+ $this->blueprint->disablePostgis();
+ }
+}
diff --git a/tests/Unit/Schema/BuilderTest.php b/tests/Unit/Schema/BuilderTest.php
new file mode 100644
index 0000000..177d4fd
--- /dev/null
+++ b/tests/Unit/Schema/BuilderTest.php
@@ -0,0 +1,39 @@
+shouldReceive('getSchemaGrammar')->once()->andReturn(null);
+
+ $mock = Mockery::mock(Builder::class, [$connection]);
+ $mock->makePartial()->shouldAllowMockingProtectedMethods();
+ $blueprint = $mock->createBlueprint('test', function() {
+ });
+
+ $this->assertInstanceOf(Blueprint::class, $blueprint);
+ }
+
+ public function testReturnsCorrectBlueprintForPostgres()
+ {
+ $connection = Mockery::mock(PostgresConnection::class);
+ $connection->shouldReceive('getSchemaGrammar')->once()->andReturn(null);
+
+ $mock = Mockery::mock(Builder::class, [$connection]);
+ $mock->makePartial()->shouldAllowMockingProtectedMethods();
+ $blueprint = $mock->createBlueprint('test', function() {
+ });
+
+ $this->assertInstanceOf(Blueprint::class, $blueprint);
+ }
+}
diff --git a/tests/Unit/Schema/Grammars/MySqlGrammarTest.php b/tests/Unit/Schema/Grammars/MySqlGrammarTest.php
new file mode 100644
index 0000000..4402b1e
--- /dev/null
+++ b/tests/Unit/Schema/Grammars/MySqlGrammarTest.php
@@ -0,0 +1,122 @@
+geometry('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('GEOMETRY', $statements[0]);
+ }
+
+ public function testAddingPoint()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->point('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('POINT', $statements[0]);
+ }
+
+ public function testAddingLinestring()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->linestring('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('LINESTRING', $statements[0]);
+ }
+
+ public function testAddingPolygon()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->polygon('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('POLYGON', $statements[0]);
+ }
+
+ public function testAddingMultipoint()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->multipoint('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('MULTIPOINT', $statements[0]);
+ }
+
+ public function testAddingMultiLinestring()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->multilinestring('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('MULTILINESTRING', $statements[0]);
+ }
+
+ public function testAddingMultiPolygon()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->multipolygon('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('MULTIPOLYGON', $statements[0]);
+ }
+
+ public function testAddingGeometryCollection()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->geometrycollection('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('GEOMETRYCOLLECTION', $statements[0]);
+ }
+
+ public function testAddRemoveSpatialIndex()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->point('foo');
+ $blueprint->spatialIndex('foo');
+ $addStatements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(2, count($addStatements));
+ $this->assertContains('alter table `test` add spatial index `test_foo_spatialindex`(`foo`)', $addStatements[1]);
+
+ $blueprint->dropSpatialIndex(['foo']);
+ $blueprint->dropSpatialIndex('test_foo_spatialindex');
+ $dropStatements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $expectedSql = 'alter table `test` drop index `test_foo_spatialindex`';
+ $this->assertEquals(5, count($dropStatements));
+ $this->assertContains($expectedSql, $dropStatements[3]);
+ $this->assertContains($expectedSql, $dropStatements[4]);
+ }
+
+ /**
+ * @return Connection
+ */
+ protected function getConnection()
+ {
+ return Mockery::mock(MysqlConnection::class);
+ }
+
+ protected function getGrammar()
+ {
+ return new MySqlGrammar();
+ }
+}
diff --git a/tests/Unit/Schema/Grammars/PostgresGrammarTest.php b/tests/Unit/Schema/Grammars/PostgresGrammarTest.php
new file mode 100644
index 0000000..ab097c2
--- /dev/null
+++ b/tests/Unit/Schema/Grammars/PostgresGrammarTest.php
@@ -0,0 +1,124 @@
+point('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('GEOGRAPHY(POINT, 4326)', $statements[0]);
+ }
+
+ public function testAddingLinestring()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->linestring('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('GEOGRAPHY(LINESTRING, 4326)', $statements[0]);
+ }
+
+ public function testAddingPolygon()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->polygon('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('GEOGRAPHY(POLYGON, 4326)', $statements[0]);
+ }
+
+ public function testAddingMultipoint()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->multipoint('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('GEOGRAPHY(MULTIPOINT, 4326)', $statements[0]);
+ }
+
+ public function testAddingMultiLinestring()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->multilinestring('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('GEOGRAPHY(MULTILINESTRING, 4326)', $statements[0]);
+ }
+
+ public function testAddingMultiPolygon()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->multipolygon('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('GEOGRAPHY(MULTIPOLYGON, 4326)', $statements[0]);
+ }
+
+ public function testAddingGeometry()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->geometry('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('GEOMETRY', strtoupper($statements[0]));
+ }
+
+ public function testAddingGeometryCollection()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->geometrycollection('foo');
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('GEOMETRYCOLLECTION', strtoupper($statements[0]));
+ }
+
+ public function testEnablePostgis()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->enablePostgis();
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('CREATE EXTENSION postgis', $statements[0]);
+ }
+
+ public function testDisablePostgis()
+ {
+ $blueprint = new Blueprint('test');
+ $blueprint->disablePostgis();
+ $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());
+
+ $this->assertEquals(1, count($statements));
+ $this->assertContains('DROP EXTENSION postgis', $statements[0]);
+ }
+
+ /**
+ * @return Connection
+ */
+ protected function getConnection()
+ {
+ return Mockery::mock(PostgresConnection::class);
+ }
+
+ protected function getGrammar()
+ {
+ return new PostgresGrammar();
+ }
+}
diff --git a/tests/Unit/Stubs/PDOStub.php b/tests/Unit/Stubs/PDOStub.php
new file mode 100644
index 0000000..99d2d80
--- /dev/null
+++ b/tests/Unit/Stubs/PDOStub.php
@@ -0,0 +1,10 @@
+&1
+}
+
+function waitContainer {
+ while STATUS=$(getContainerHealth $1); [[ "$STATUS" != '"healthy"' ]]; do
+ if [[ "$STATUS" == $'\nTemplate parsing error'* ]]; then
+ echo " $1 Failed!"
+ exit 255
+ fi
+ if [[ "$STATUS" == '"unhealthy"' ]]; then
+ docker inspect --format "{{json .State }}" $1 2>&1
+ else
+ echo -n .
+ fi
+ sleep 5
+ done
+}
+
+waitContainer $@