GithubHelp home page GithubHelp logo

laravel-addresses's Introduction

Latest Stable Version Total Downloads License

Laravel Addresses

Simple address and contact management for Laravel with automatical geocoding to add longitude and latitude.

Installation

Require the package from your composer.json file

"require": {
	"lecturize/laravel-addresses": "^1.1"
}

and run $ composer update or both in one with $ composer require lecturize/laravel-addresses.

Configuration & Migration

$ php artisan vendor:publish --provider="Lecturize\Addresses\AddressesServiceProvider"

This will publish the config file to config/lecturize.php and some migration files, that you'll have to run:

$ php artisan countries:migration
$ php artisan migrate

For migrations to be properly published ensure that you have added the directory database/migrations to the classmap in your projects composer.json.

Check out Webpatser\Countries readme to see how to seed their countries data to your database.

Usage

First, add our HasAddresses trait to your model.

<?php namespace App\Models;

use Lecturize\Addresses\Traits\HasAddresses;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasAddresses;

    // ...
}
?>
Add an Address to a Model
$post = Post::find(1);
$post->addAddress([
    'street'     => '123 Example Drive',
    'city'       => 'Vienna',
    'post_code'  => '1110',
    'country'    => 'AT', // ISO-3166-2 or ISO-3166-3 country code
    'is_primary' => true, // optional flag
]);

Alternativly you could do...

$address = [
    'street'     => '123 Example Drive',
    'city'       => 'Vienna',
    'post_code'  => '1110',
    'country'    => 'AT', // ISO-3166-2 or ISO-3166-3 country code
    'is_primary' => true, // optional flag
];
$post->addAddress($address);

Available attributes are street, street_extra, city, post_code, state, country, state, notes (for internal use). You can also use custom flags like is_primary, is_billing & is_shipping. Optionally you could also pass lng and lat, in case you deactivated the included geocoding functionality and want to add them yourself.

Check if Model has Addresses
if ($post->hasAddresses()) {
    // Do something
}
Get all Addresses for a Model
$addresses = $post->addresses()->get();
Get primary/billing/shipping Addresses
$address = $post->getPrimaryAddress();
$address = $post->getBillingAddress();
$address = $post->getShippingAddress();
Update an Address for a Model
$address = $post->addresses()->first(); // fetch the address

$post->updateAddress($address, $new_attributes);
Delete an Address from a Model
$address = $post->addresses()->first(); // fetch the address

$post->deleteAddress($address); // delete by passing it as argument
Delete all Addresses from a Model
$post->flushAddresses();

Contacts

First, add our HasContacts trait to your model.

<?php namespace App\Models;

use Lecturize\Addresses\Traits\HasContacts;
use Illuminate\Database\Eloquent\Model;

class Team extends Model
{
    use HasContacts;

    // ...
}
?>
Add a Contact to a Model
$post = Team::find(1);
$post->addContact([
    'first_name' => 'Alex',
    'website'    => 'https://twitter.com/AMPoellmann',
    'is_primary' => true, // optional flag
]);
Relate Addresses with Contacts

Above all, addresses and contacts can be connected with an optional One To Many relationship. Like so you could assign multiple contacts to an address and retrieve them like so:

$address = config('lecturize.addresses.model', \Lecturize\Addresses\Models\Address::class)::find(1);
$contacts = $address->contacts;

foreach ($contacts as $contact) {
    //
}
$contact = config('lecturize.contacts.model', \Lecturize\Addresses\Models\Contact::class)::find(1)
                  ->contacts()
                  ->first();
$contact = config('lecturize.contacts.model', \Lecturize\Addresses\Models\Contact::class)::find(1);

return $contact->address->getHtml();
Geocoding

The address model provides a method geocode() which will try to fetch longitude and latitude through the Google Maps API. Please make sure to add your key within the services config file at services.google.maps.key. If you set the option lecturize.addresses.geocode to true, the package will automatically fire the geocode() method whenever an addresses model is saved (precisely we hook into the saving event).

Changelog

  • [2021-02-02] v1.0 The geocode configuration option now defaults to false.
  • [2022-05-16] v1.1 Updated dependencies to PHP 8 and Laravel 8/9 - for older versions please refer to v1.0.
  • [2023-02-21] v1.2 Laravel 10 support.
  • [2023-09-21] v1.3 Support custom models for addresses and contacts, thanks to @bfiessinger. The geocoding feature now requires a Google Maps key, see 'Geocoding' above. Also, @bfiessinger has added fallback support for flags, see pull request #40 for further info.
  • [in-progress] v1.4 Added additional contact fields to the addresses table, to allow for easier standalone usage (without the contacts model). This is intended to reduce the complexity of relationships and queries, that before were necessary e.g. to generate a shipping label from address and contact.

License

Licensed under MIT license.

Author

Handcrafted with love by Alexander Manfred Poellmann in Vienna & Rome.

laravel-addresses's People

Contributors

alexanderpoellmann avatar bfiessinger avatar jonerickson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

laravel-addresses's Issues

HasContacts::flushAddresses() bug (typo)

The flushContacts method is calling contactes when it should be calling the correctly named contacts morphMany relationship method.

Patch to fix:

--- /tmp/HasContacts.php	2019-12-31 13:44:18.000000000 -0700
+++ /tmp/HasContacts.php2	2019-12-31 13:44:54.000000000 -0700
@@ -77,13 +77,13 @@
     }

     /**
-     * Deletes all the contactes of this model.
+     * Deletes all the contacts of this model.
      *
      * @return bool
      */
     public function flushContacts()
     {
-        return $this->contactes()->delete();
+        return $this->contacts()->delete();
     }

     /**
@@ -120,4 +120,4 @@

         return validator($attributes, $rules);
     }
-}
\ No newline at end of file
+}

I'll create a PR for this when I have time.

Migrations are generated out of order

When running

$ php artisan vendor:publish --provider="Lecturize\Addresses\AddressesServiceProvider"

Migrations are created but not necessarily in the correct order. This leads to AddStreetExtraToAddressesTable being executed before the table is created.

Workaround:
By changing the times of the filenames they can be reordered so create_addresses_table and create_contacts_table are executed first.

Address flags should fallback

Using methods like $model->getPrimaryAddress() or $model->getSomeFlagAddress() only return the desired results when the flags are explicitly set on the Model's addresses while I think they should provide some fallback functionality.

Small example:
Let's say we want to determine the primary Address of a model that has 2 Addresses attached, but none of them has the primary flag.
Imo in this example we should fallback to the first address attached here other than returning no result.

Another one:
What if we want to determine the billing or shipping address of a model where only one of the attached addresses has a flag, which in this case is the primary one. Here I think billing and shipping might fallback to the primary address.

What to do?
I think we should provide some fallback logic in form of configuration, this could look like something below e.g.:

...
/*
 * Flag columns to be added to table.
 */
'flags' => ['public', 'primary', 'billing', 'shipping'],
'flag_fallbacks' => [
    'default' => ['primary', 'billing', 'shipping'],
    'primary' => ['billing', 'shipping'],
   ...
],
...

The functionality could be done by providing something like this to the HasAddresses Trait: (not tested yet, just for showcasing the idea)

    // call get{Flag}Address() to get the first address with the flag set to true
    public function __call($method, $parameters)
    {
        if (preg_match('/^get(.+)Address$/', $method, $matches)) {
            $flag = strtolower($matches[1]);

            return $this->getAddressByFlag($flag);
        }

        return parent::__call($method, $parameters);
    }

    /**
     * Get the address by flag.
     *
     * @param  string  $flag
     * @param  string  $direction
     * @return Address|null
     */
    public function getAddressByFlag($flag = null, $direction = 'desc'): ?Address
    {
        if (! $this->hasAddresses())
            return null; // short circuit if no addresses exist

        $address = $this->addresses();
        if ($flag !== null) {
            $address = $address->where('is_' . $flag, true);
        }
        $address = $address->orderBy('is_' . $flag, $direction)
            ->first();

        if ($address)
            return $address;

        $fallbacks = config('lecturize.addresses.flag_fallbacks', []);
        if (isset($fallbacks[$flag])) {
            foreach ($fallbacks[$flag] as $fallback) {
                $address = $this->getAddressByFlag($fallback, $direction);
                if ($address) {
                    return $address;
                }
            }
        }

        if (! $address && $flag !== null) {
            $address = $this->getAddressByFlag(null, $direction);
        }

        return $address;
    }

Let's discuss about the idea and try to improve it in the comments. I'm ready for making this a PR.

updateAddress expects all address attributes

Hi there,

In the documentation I see the following code for updating an existing address:

$post->updateAddress($address, $new_attributes);

So I added a variable to update the street address. Like this:

$new_attributes = [
  'street'     => '123 Update Drive',
];

Is it correct that this does not work and I need to add the existing attributes as well? I now merge the existing address array with my new_attributes array. Is that how I should do it?

Mark

Laravel5 Dependecy Error

I get this error :
Call to undefined method Illuminate\Database\Query\Builder::addAddress()

I now that i need to call the function in top of controller
like so
use ....Addresses;

But i don't know what is before the Addresses;
Any one knows?
Thanks

Add street_extra

protected $fillable = [
        'street',
           'street_extra'
        'city',
        'state',
        'post_code',
        'country_id',
        'lat',
        'lng',
        'addressable_id',
        'addressable_type',
        'is_primary',
        'is_billing',
        'is_shipping',
    ];

No Nullables... MUST add Note, but then it's not defined and throws a MySQL error?

                $address = $user->addAddress([
			'street'     => request('address'),
			'city'       => request('city'),
			'post_code'  => request('post_code'),
			'country_id' => request('country'),
			'state'      => '',
			'note'       => 'No note.',
		]);

This is my code, yet it throws an error?

SQLSTATE[HY000]: General error: 1364 Field 'note' doesn't have a default value (SQL: insert into addresses (street, city, post_code, country_id, state, addressable_id, addressable_type, lat, lng, updated_at, created_at) values (street, postcode, countryid, , 1, App\User, lat, long, 2017-06-15 19:42:10, 2017-06-15 19:42:10))

Geolocation not being added automatically

I recently installed this library to a Laravel app, however, after adding the address for a model, I'm still unable to get the lat/lng values to update.

image

I have updated the configuration to enable geolocation:
image

The installation was quite basic and I didn't have any issues getting the country seeder to work and adding addresses has been quite straightforward.

Here is my dependencies:

image

Method deleteAddress is not working on php 7.2

This condition:

$this !== $address->addressable()->first()

is always true so the function always returns false and the address is never deleted.

Changing this with:

$this != $address->addressable()->first()

...or with:

$this->id !== $address->addressable()->first()->id

works.

Custom attributes in the Address table

Is there a way to add custom attributes to the Address table? In the past I thought I was able to do so but it seems if I add anything else now in the attributes list, it is stored as NULL.

$address = $request->user()->addAddress([
            'full_name'    => $request->full_name,   // NULL
            'company'      => $request->company,  // NULL
            'street'       => $request->street,
            'street_extra' => $request->street_extra,
            'city'         => $request->city,
            'state'        => $request->state,
            'country_id'   => 840, // US Country Code
            'post_code'    => $request->post_code,
            'phone'        => $request->phone,     // NULL
            'is_shipping'  => $request->is_shipping, 
            'is_billing'   => $request->is_billing,
        ]);

How to get models by address?

Is it possible to get model by address? If so how?
for example i can access to $post->addresses.
How can i get all posts related to specific address(Country Or City)?

Validations fail

For example, this:


$association->addAddress([
        'street'     => 'Sadama 1',
	'city'       => 'Tartu,
	'post_code'  => '51004',
	'country'    => 'EE',
]);

will fail. Also tried with your own addresses from the readme. They also fail.


Seeding: AssociationSeeder

   Lecturize\Addresses\Exceptions\FailedValidationException  : Validator failed for: 123 Example Drive, Vienna, 1110, 1

  at /home/rando/Webdev/aparte/vendor/lecturize/laravel-addresses/src/Traits/HasAddresses.php:149
    145|         // run validation
    146|         $validator = $this->validateAddress($attributes);
    147| 
    148|         if ($validator->fails())
  > 149|             throw new FailedValidationException('Validator failed for: '. implode(', ', $attributes));
    150| 
    151|         // return attributes array with country_id key/value pair
    152|         return $attributes;
    153|     }

  Exception trace:

  1   App\Association::loadAddressAttributes(["123 Example Drive", "Vienna", "1110"])
      /home/rando/Webdev/aparte/vendor/lecturize/laravel-addresses/src/Traits/HasAddresses.php:43

  2   App\Association::addAddress(["123 Example Drive", "Vienna", "1110", "AT"])
      /home/rando/Webdev/aparte/database/seeds/AssociationSeeder.php:24

Migration Issues

while migrating issue in renaming note to notes.
add attribute to address

$timestamp is always the same.

private function handleMigrations()
{
foreach ($this->migrations as $class => $file) {
if (! class_exists($class)) {
$timestamp = date('Y_m_d_His', time());
$this->publishes([
__DIR__ .'/../database/migrations/'. $file .'.php.stub' =>
database_path('migrations/'. $timestamp .'_'. $file .'.php')
], 'migrations');
}
}
}

$timestamp should be increased for every migration.

Contact HasCountry Trait

I noticed the Contact model has the HasCountry trait

Should this be there? I don't see a country ID in the DB.
I would assume the country should only be on the address that is linked to a contact.

Any insights on why this is here would be great.

Concept behind contacts

Hi there,

I currently test your package. As I currently have to build a pretty generic API I focus several issues.

Currently I have a customer model (as one of the main factors will be eCommerce usage).
While installing your package I noticed that you migrated a contacts table.
On first glance however I don't see that its used.

Is contact intended to be used in a similar manner as the address or is it basically the same as my customers.

So either:
Customer/Contact can have an address
or
Customer (or whatever) can have a contact which itself can have a address.
contactable_id and contactable_type seem to suggest that.

Would love to get some clarification on that.

Property "is_public" is not fillable

Hi
The is_public property is not being updated when calling updateAddress method.
Seems that it's not included in $fillable array of Address class.

Thanks for nice job.

Error in Class AddStreetExtraToAddresses Class

Actually, in the migration file Datetime_Add_Street_Extra_To_Addreses.php:
The Classname is :

/**
 * Class AddStreetExtraToAddresses
 */
class AddStreetExtraToAddresses extends Migration
{

This generate an error when running the php artisan migrate command :

In Migrator.php line 418:

Class 'AddStreetExtraToAddressesTable' not found

By adding Table at the end of the name of the class resolve the problem
class AddStreetExtraToAddressesTable

Empty values in laravel 5.4

It seems there is an compatibility issue with Laravel 5.4 (maybe other versions as well).

The addresses where created with empty values.

I suspected that it is the same problem as this.

I tested the solution and it worked for me. The change as seen in the link is simply changing
this:

    public function __construct()
    {
        parent::__construct();
        $this->table = config('lecturize.addresses.table', 'addresses');
    }

to this:

    public function __construct(array $attributes = array())
    {
        parent::__construct($attributes);
        $this->table = config('lecturize.addresses.table', 'addresses');
    }

A colleague mentioned ( I do not have any link so far to support this claim) that things like this should be handled in boot and not constuct anymore.

So maybe there even is a more up to date method to pass the attributes.

How to overide code to add city from external table

Hi Alexander, I am using your package but I have a little need to override the code. I am looking to make a separate table for cities in which I will have only id and city name. I want to have only city_id in your logic. What is the best way to do it? I do not want to change in vendor folders to avoid problems in future.

I will highly appreciate your suggestions..

add_Street_extra_to_addresses_table and create_adresses_table migration conflict

These two files contains the same code except this lines :

        Schema::table($this->table, function(Blueprint $table){
            $table->string('street_extra', 60)->nullable()->after('street');
        });

find in the add_street_extra_to_addresses_table file.
So after run the migration, the create_adresses_table stop the process as it try to create the addresses table again.

[Illuminate\Database\QueryException]
SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'addresses' already exists (SQ
L: create table addresses (id int unsigned not null auto_increment primary key, street
varchar(60) null, city varchar(60) null, state varchar(60) null, post_code varchar(10)
null, country_id int unsigned null, note varchar(191) null, lat double(10, 6) null, ln g double(10, 6) null, addressable_id int unsigned null, addressable_type varchar(191) nu
ll, is_public tinyint(1) not null default '0', is_primary tinyint(1) not null default '0'
, is_billing tinyint(1) not null default '0', is_shipping tinyint(1) not null default '0'
, created_at timestamp null, updated_at timestamp null, deleted_at timestamp null) defa
ult character set utf8mb4 collate utf8mb4_unicode_ci)

[PDOException]
SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'addresses' already exists

Either the add_street_extra_to_addresses_table has to come after the create_adresses_table with this code

   if(!Schema::hasTable($this->table)){
        Schema::create($this->table, function(Blueprint $table)
        {
            $table->increments('id');

            $table->string('street',    60)->nullable();
            $table->string('city',      60)->nullable();
            $table->string('state',     60)->nullable();
            $table->string('post_code', 10)->nullable();

            $table->integer('country_id')->nullable()->unsigned()->index();

            $table->string('note')->nullable();

            $table->float('lat', 10, 6)->nullable();
            $table->float('lng', 10, 6)->nullable();

            $table->nullableMorphs('addressable');

            foreach(config('lecturize.addresses.flags', ['public', 'primary', 'billing', 'shipping']) as $flag) {
                $table->boolean('is_'. $flag)->default(false)->index();
            }

            $table->timestamps();
            $table->softDeletes();
        });
    }
        Schema::table($this->table, function(Blueprint $table){
            $table->string('street_extra', 60)->nullable()->after('street');
        });

either we should add the creation of the street_exta field in the create addresses table file.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.