Laravel 5 Step by Step Guide to Vagrant Homestead in Mac

Well, if you are looking for a step by step guide but just found the older one, you might be lucky because i have just setup mine with Vagrant Homestead which is most likely what you are looking for as well. In short, the one explain on their official website isn't very clear that i have to get out and read other article just to set this up. So for anyone convenient and for mine as well, i'll just quickly write down what i did and what makes it magically work in this step by step guide.

Install required scripts

Before you even decide to setup anything you will need to do install a number of scripts to help in setting up Homestead Vagrant.

Install Vagrant

First, go ahead and visit Vagrant official website and download the mac version of Vagrant and get it install on your machine
Screen Shot 2015-10-19 at 4.32.05 AM
Just click on the pkg and continue till the end and we are all good with Vagrant.

Install VirtualBox

Without VirtualBox don't think about getting Vagrant to work. Therefore, head over to VirtualBox official website.
Screen Shot 2015-10-19 at 5.20.46 AM

Similarly, open up the .dmg file of virtualbox and install it via .pkg file and continue till the end and we are all good with VirtualBox.

Install Composer

Now to install Composer we will need to do this. to get composer available globally.

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

However, make sure your 'php' is available as shown here,

Clays-MacBook-Air:public clay$ whereis php
/usr/bin/php

If not, please try to install php first before doing this.

Install Laravel

Now i wanted to use the script 'laravel new xxx' so i have to install Laravel as well by firing the following composer script.

composer global require "laravel/installer=~1.1"

Once this is done you should be able to create a new laravel website by generating the base files of laravel

laravel new blog

but be patient, don't do this first since we really want to set this up with Vagrant and Homestead.

Install Vagrant Homestead Box

Now we need to install Homestead Vagrant box. We can first download this upfront so we could use it later.

vagrant box add laravel/homestead

Once you've done that you can see it via

Clays-MacBook-Air:public clay$ vagrant box list
laravel/homestead (virtualbox, 0.3.0)

which should list out what vagrant boxes you have.

Install Homestead CLI

Now before anything else, we will need to install Homestead CLI tool.

composer global require "laravel/homestead=~2.0"

and make sure the path ~/.composer/vendor/bin is available after installation. And also add them into $PATH via .bashrc or .profile whichever you but might is located at ~/.profile

export PATH=~/.composer/vendor/bin:$PATH

and add this line so that we can use whatever is in the vendor bin folder.

Setup Homestead YAML file

Now we have finish installed all required script for Laravel and Vagrant. We need to setup Homestead YAML file but first, we will need to create the configure file via

homestead init
Creating Homestead.yaml file... ok
Homestead.yaml file created at: /Users/claylua/.homestead/Homestead.yaml

Now we know where this file is located, we just need to open up and starts configuring some important parts.

vi ~/.homestead/Homestead.yaml

i use vim, you can use nano and etc or whatever you like. And the most important parts are shown below,

// this is the public key
authorize: ~/.ssh/id_rsa.pub

// this is my private key that i normally use
keys:
- ~/.ssh/id_rsa

// in my macbook, i am playing all laravel files on ~/Vagrant/Code (i created it myself)
// and on Vagrant VM iw ill place it on  /Users/claylua/Vagrant/Code
folders:
- map: ~/Vagrant/Code
  to: /Users/claylua/Vagrant/Code

// i am mapping the Laravel files to claylua.com and on vagrant of vm, it will look for the 
// directory located at /Users/claylua/Vagrant/Code/blog/public
sites:
- map: claylua.com
  to: /Users/claylua/Vagrant/Code/blog/public

// i change the name of the database
databases:
- claylua

so edit your configuration file accordingly and to your liking and we can start creating Laravel base files.

Setup Laravel Base Files

As you can see on my Homestead.yaml file, i have tell the configuration to go to the directory ~/Vagrant/Code where i will setup Laravel there.

mkdir -p ~/Vagrant/Code

the above will create the folder needed.

cd ~/Vagrant/Code
laravel new blog

The above will create a new laravel blog folder.

Setup Vagrant

Now we have finish setup both laravel files and homestead configuration. It's time to start Vagrant and access our website via

homestead up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'laravel/homestead'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'laravel/homestead' is up to date...
...
==> default: Forwarding ports...
default: 80 => 8000 (adapter 1)
default: 443 => 44300 (adapter 1)
default: 3306 => 33060 (adapter 1)
default: 5432 => 54320 (adapter 1)
default: 22 => 2222 (adapter 1)

Once this is done, do remember that i have setup 'claylua.com' to point to my laravel installation folder. Hence, i will need to setup this domain to point it to my local machine by doing this

sudo vi /etc/hosts

and add this line at the end

192.168.10.10 claylua.com

If you are wondering why its 192.168.10.10 its because on my homestead.yaml i have place it as this ip address. Once you've done that, all you need to do is to open up the url 'claylua.com' and you'll see the following display
Screen Shot 2015-10-19 at 4.57.36 AM
and if you don't like what you see. Just destory it via command line

homestead destroy

and if you find it too troublesome to hit one line then open up VirtualBox and 'stop and remove' it.
Screen Shot 2015-10-19 at 4.58.51 AM
And you can redo the `homestead up` again.

SSH into Vagrant VM

Since this is all VM, which is really another small machine on your physical machine, you can actually ssh into it and install any thing you want. By default you can ssh via

ssh [email protected] -p 2222
Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.19.0-25-generic x86_64)

 * Documentation:  https://help.ubuntu.com/
Last login: Sun Oct 18 20:14:08 2015 from 10.0.2.2

and you should be in as shown above.

Connect to Vagrant Database

Now we need to see whether we can login to our mysql on Vagrant homestead machine. I'm using Sequel Pro and the default value are

Host: 127.0.0.1
User: homestead
Password: secret
Port: 33060

And if you click on 'test connection' you should see this,
Screen Shot 2015-10-19 at 5.02.32 AM
Pretty neat ya.

Environment Variable

In the case you want to send environment variable to your vagrant homestead setup, all you need to do is to open up homestead.yaml and place these new variables in

variables:
- key: APP_ENV
value: dev
- key: API_KEY
value: nudeinbath
- key: API_SECRET
value: dontlookatmelikethatyouasshole

And in your Laravel application you can use this variable like this

$app_env = getenv('APP_ENV'); // returns "dev"
$api_key = getenv('API_KEY'); // returns "nudeinbath"
$api_secret = getenv('API_SECRET'); // returns "dontlookatmelikethatyouasshole"

Adding New Sites

So you have nothing to do and wanted to add a new website to create new laravel project. Simply head over to the Homestead.yaml file and add it below,

// i am mapping the Laravel files to claylua.com and on vagrant of vm, it will look for the 
// directory located at /Users/claylua/Vagrant/Code/blog/public
sites:
- map: claylua.com
  to: /Users/claylua/Vagrant/Code/blog/public
// now i am adding a new website call shit
- map: shit.com
  to: /Users/claylua/Vagrant/Code/shit/public

Similarly, i will need to add a new laravel folder into it

cd ~/Vagrant/Code
laravel new shit

Now i have to provision my server again and map the new locations from your Homestead directory.

vagrant provision

and this doesn't work you'll do this

vagrant reload --provision

And you will see something like this

Clays-MacBook-Air:.homestead clay$ vagrant reload --provision
==> default: Attempting graceful shutdown of VM...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 => 2222 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: The guest additions on this VM do not match the installed version of
    default: VirtualBox! In most cases this is fine, but in rare cases it can
    default: prevent things such as shared folders from working properly. If you see
    default: shared folder errors, please make sure the guest additions within the
    default: virtual machine match the version of VirtualBox you have installed on
    default: your host and reload your VM.
    default:
    default: Guest Additions Version: 5.0.4
    default: VirtualBox Version: 4.3
==> default: Mounting shared folders...
    default: /vagrant => /Users/clay/.homestead

And if the provision still doesn't work, do this

Clays-MacBook-Air:.homestead clay$ homestead halt
==> default: Attempting graceful shutdown of VM...
Clays-MacBook-Air:.homestead clay$ homestead up --provision
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'laravel/homestead' is up to date...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 80 => 8000 (adapter 1)
    default: 443 => 44300 (adapter 1)
    default: 3306 => 33060 (adapter 1)
    default: 5432 => 54320 (adapter 1)
    default: 22 => 2222 (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: The guest additions on this VM do not match the installed version of
    default: VirtualBox! In most cases this is fine, but in rare cases it can
    default: prevent things such as shared folders from working properly. If you see
    default: shared folder errors, please make sure the guest additions within the
    default: virtual machine match the version of VirtualBox you have installed on
    default: your host and reload your VM.
    default:
    default: Guest Additions Version: 5.0.4
    default: VirtualBox Version: 4.3
==> default: Setting hostname...
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => /Users/clay/.composer/vendor/laravel/homestead
    default: /Users/clay/Vagrant/Code => /Users/clay/Vagrant/Code
==> default: Running provisioner: file...
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: Running provisioner: shell...
    default: Running: /var/folders/7h/k2cjyqms7rxfnv3mvl_b0pq00000gn/T/vagrant-shell20151102-10803-lxzddo.sh
==> default: Running provisioner: shell...
    default: Running: /var/folders/7h/k2cjyqms7rxfnv3mvl_b0pq00000gn/T/vagrant-shell20151102-10803-1xy8ywc.sh
==> default: nginx stop/waiting
==> default: nginx start/running, process 1659
==> default: php5-fpm stop/waiting
==> default: php5-fpm start/running, process 1674
==> default: Running provisioner: shell...
    default: Running: /var/folders/7h/k2cjyqms7rxfnv3mvl_b0pq00000gn/T/vagrant-shell20151102-10803-12bufkv.sh
==> default: nginx stop/waiting
==> default: nginx start/running, process 1712
==> default: php5-fpm stop/waiting
==> default: php5-fpm start/running, process 1727
==> default: Running provisioner: shell...
    default: Running: /var/folders/7h/k2cjyqms7rxfnv3mvl_b0pq00000gn/T/vagrant-shell20151102-10803-qs9lku.sh
==> default: Warning: Using a password on the command line interface can be insecure.
==> default: Running provisioner: shell...
    default: Running: /var/folders/7h/k2cjyqms7rxfnv3mvl_b0pq00000gn/T/vagrant-shell20151102-10803-13bnnt7.sh
==> default: createdb: database creation failed: ERROR:  database "temprole" already exists
==> default: Running provisioner: shell...
    default: Running: /var/folders/7h/k2cjyqms7rxfnv3mvl_b0pq00000gn/T/vagrant-shell20151102-10803-xzs2bs.sh
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: php5-fpm stop/waiting
==> default: php5-fpm start/running, process 1816
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: Updating to version 5a5088eb342e4876cb28472ba1fc3f1da7a14852.
==> default:     Downloading: Connecting...
==> default:
==> default:     Downloading: 100%
==> default:
==> default:
==> default: Use composer self-update --rollback to return to version a54f84f05f915c6d42bed94de0cdcb4406a4707b
==> default: Running provisioner: shell...
    default: Running: /var/folders/7h/k2cjyqms7rxfnv3mvl_b0pq00000gn/T/vagrant-shell20151102-10803-88fx5m.sh

but remember to save the new shit.com to your hosts file

sudo vi /etc/hosts

now let's add this shit

192.168.10.10 shit.com

And we are done!

Conclusion on Laravel 5

Pretty much sweet and easy installation with a large community backed up with it. I'm sure there will be more fun ahead! Enjoy your Laravel 5!

Mongodb adding user access for authentication on remote server

By default if you install mongodb into your server, it doesn't automatically add a default user or enable authentication. However, you might wan to add in authentication on your Mongodb configuration once you have more than one database. Before you do anything, we first needs to add user into our collection.

Mongodb adding user access

In order to add a new user, we will just have to access our mongodb without password on the command line,

[root@data ~]# mongo
MongoDB shell version: 3.0.0
connecting to: test
Server has startup warnings:
2015-10-10T18:45:14.364+0800 I CONTROL  [initandlisten]
2015-10-10T18:45:14.364+0800 I CONTROL  [initandlisten] ** WARNING: You are running in OpenVZ which can cause issues on versions of RHEL older than RHEL6.
2015-10-10T18:45:14.364+0800 I CONTROL  [initandlisten]

Ok, for admin user, you might need to do the following

> use admin
switched to db admin

Now in order to manage everything you need to do the following

db.createUser( {
    user: "uptime",
    pwd: "Basketball10",
    roles: [ { role: "root", db: "admin" } ]
  });

As you can see i did not have any password enable. Next, we want to add this user to mongodb and the collection access i want to give my user to is call 'storage', so i'm going to switch to storage directly.

> use storage
switched to db storage

In order to add a new user with read and write permission. All i have to do is to fire the below command.

db.createUser(
    {
      user: "user",
      pwd: "password",
      roles: [
         { role: "readWrite", db: "storage" },
         { role: "read", db: "shopping" }
      ]
    }
);

take note that the 'role', the permission available are 'readWrite', 'read' and 'write'. And the 'db' is basically the database allowed for this particular added user. I have added read for shopping database and readWrite for storage for this particular 'user'.

Let's test this before we go to the next step

db.auth("user", "password")
>1

where 1 refer to valid and 0 refer to invalid. Now we will need to change our mongodb to an auth mode by going to /etc/mongod.conf

# for version below 3.0
# Turn on/off security.  Off is currently the default
#noauth=true
#auth=true

# for version above 3.0 - YAML based
#security:
#	authorization: enabled

look for this line and uncomment the it which will gives you the below configure file

# for version below 3.0
# Turn on/off security.  Off is currently the default
#noauth=true
#auth=true

# for version above 3.0 - YAML based
security:
	authorization: enabled

now all we need to do is to restart the service

[root@data ~]# service mongod restart
Stopping mongod:                                           [  OK  ]
Starting mongod:                                           [  OK  ]

and try it out by firing on your command line the following

mongo data.hungred.com:27017/storage -u user -p password

change your data.hungred.com:27017 to your own port and url as you will know, this will not work for you.

Disable delete for Woocommerce order

I have some problem recently with Woocommerce order being deleted by Admin and we have no idea who did it. And that annoys me. Hence, i went ahead and disable Woocommerce delete for any order for all users. Pretty much i do not want any delete to happen for any order regardless it is intentionally or accidentally.

Disable Woocommerce Delete

In order to disable woocommerce delete for order, you need to hook in to the 2 action hook which is wp_tash_post and before_delete_post as shown below,

// disable delete entirely
function restrict_post_deletion($post_ID){
    $type = get_post_type($post_ID);
    if($type == 'shop_order'){
        echo "You are not authorized to delete this page.";
        exit;
    }
}
add_action('wp_trash_post', 'restrict_post_deletion', 10, 1);
add_action('before_delete_post', 'restrict_post_deletion', 10, 1);

what the above does is to get the post_id of the post you are deleting and see what type it is. If the type if a shop_order, do not allow them to delete by exiting the script entirely and show a message to the user so that they will stop doing silly things.

Woocommerce After Checkout Hook

Here is another Woocommerce hook that i used recently that is directly call after checkout but before an order is made, it is a Woocommerce after checkout hook. In this case, you can add more validation into it to prevent the order from being creating it. To do this, all you need to do is use the hook callĀ 'woocommerce_after_checkout_validation' which gets call after checkout validation is made.

  add_action( 'woocommerce_after_checkout_validation', 'remove_item_cart_session_expired' );
  function remove_item_cart_session_expired(){
    global $woocommerce;
    $data = WC()->session->get('mypersonalsession');
    if(!$data){
      $woocommerce->cart->empty_cart();
      wc_add_notice( __("<strong>ERROR:</strong> Code 1010 - Your session expired. Please reorder again", "test"), "error" );
    }

In this case, the validation is already completed but my session has expired. So i added an error notice so that my team can look into the error code and figure out what has gone wrong with this particular order. Of course, i emepty the cart so that the order cannot be create!

Woocommerce After an Order Before Payment Hook

There are times when you want to do some action like sending the order info to another database or third party integration before payment took place. of course, after payment took place, you might still initial another type of hook which is not explain here, there are tons of them if you google around. However, before a payment is made and after a checkout is place, an order is created. This hook is often ignored and not mention around. And this is the Woocommerce hook i am going to demonstrate here.

2 Woocommerce action hook after an order is made

There are actually two action hook that you can use here which are

woocommerce_checkout_order_processed
woocommerce_new_order

Both this hook, allows you to initial your custom function immediately after an order is made such as the one below,

  add_action( 'woocommerce_checkout_order_processed', 'my_status_pending',  1, 1  );

or

  add_action( 'woocommerce_new_order', 'my_status_pending',  1, 1  );

Do bear in mind that the priority here is placed at the highest as compare to the default 10. If you just use the default priority, the chances of your payment getting directly marked as paid rather than going through the normal process of Woocommerce is high. It happens to me as i couldn't figure out how come all order are being marked as paid without going through any payment selected. It is all due to the action hook used above which you have to take note of the priority to prevent yourself losing money due to unpaid invoice marked as paid automatically.

New order hook

The argument parameter is marked as 1 which is the default since we only really need the order id of the hook as shown below,

add_action( 'woocommerce_new_order', 'my_status_pending',  1, 1  );
function my_status_pending($order_id){
// do your magic here
}

Do remember to NOT place any $woocommerce->cart->empty_cart() sentence within these methods as it will remove the item in the cart and leave other checkout method unable to proceed further. Leave the empty_cart instruction to the payment gateway to handle it.