Cron Job in Docker Container

I have created a cron job for my project recently. At the beginning, I created it on the host with the command:

* * * * * cd ~/duy/project/test/app && php artisan schedule:run >> /dev/null 2>&1

But it seems my cron was not working. It takes me over 10 mins to figured out that I was on the local machine, so I guessed my php artisan command was not working. So I jumped into the Docker container which contains my source code Laravel and ran above command again. But I got no luck. After 30 mins, my work was still stocking. Therefore, I had to take a break. I got a hot cup of local tea. Drunk it and no longer thinking about the cron job problem is.

At the evening, I came back to the work. I thought I need to know what was going on behind the scene. So I wrote the command line which echoes the test text said “OK” which inserted into a log file every the Cron invoked. Ran the Cron again and checked the log file. Nothing was there. So I researched around on Google. I got this comment on stackoverflow.com https://stackoverflow.com/a/44958097/4184355. Following this post, I have rebuilt my Docker container with including few RUN commands.

I added cron, nano editor to the webserver and a test cronjob.

RUN apt-get update && apt-get -y install cron nano
RUN (crontab -l ; echo "* * * * * echo "Hello world" >> /var/www/html/log.txt") | crontab

If the cronjob work, It will write the “Hello world” text every minute into the log.txt file. Then, I was waiting for a few minutes and came back to check the log.txt file. No text was there :(. I kept googling. I found out that I could check whether the cronjob working or not by the command service cron status. Then it said:


Finally, I found this post https://www.bigbrus.com/2015/12/15/cron-death-cant-lock-varruncrond-pid-otherpid-may-be/. The post showed me a few solutions. I tried to one of them kill -9 $(cat /var/run/crond.pid)

Then, I checked the cronjob again by the command service cron status. Now it showed ok.

To ensure that the cronjob is working fine, I opened the log.txt file. There were many “Hello world” in there. So my problem solved. The next part is appending the cronjob for the schedule command Laravel.

Invalid default value for Datetime type in MySQL

If you generated the script from the MySQL workbench.

The following line is generated

SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

Remove TRADITIONAL from the SQL_MODE, and then the script should work fine

Else, you could set the SQL_MODE as Allow Invalid Dates

SET SQL_MODE='ALLOW_INVALID_DATES';

Difference between method calls $model->relation() and $model->relation

Short answer

$model->relation() returns the relationship object

$model->relation returns the result of the relationship

Long answer

$model->relation() can be explained pretty simple. You’re calling the actual function you defined your relation with. Yours for distributor probably looks somewhat like this:

public function distributors(){
    return $this->hasMany('Distributor');
}

So when calling $store->distributors() you just get the return value of $this->hasMany('Distributor') which is an instance of Illuminate\Database\Eloquent\Relations\HasMany

When do you use it?

You usually would call the relationship function if you want to further specify the query before you run it. For example add a where statement:

$distributors = $store->distributors()->where('priority', '>', 4)->get();

Of course you can also just do this: $store->distributors()->get() but that has the same result as $store->distributors.


Which brings me to the explanation of the dynamic relationship property.

Laravel does some things under the hood to allow you to directly access the results of a relationship as property. Like: $model->relation.

Here’s what happens in Illuminate\Database\Eloquent\Model

1) The properties don’t actually exist. So if you access $store->distributors the call will be proxied to __get()

2) This method then calls getAttribute with the property name getAttribute('distributors')

public function __get($key)
{
    return $this->getAttribute($key);
}

3) In getAttribute it checks if the relationship is already loaded (exists in relations). If not and if a relationship method exists it will load the relation (getRelationshipFromMethod)

public function getAttribute($key)
{
    // code omitted for brevity

    if (array_key_exists($key, $this->relations))
    {
        return $this->relations[$key];
    }

    $camelKey = camel_case($key);

    if (method_exists($this, $camelKey))
    {
        return $this->getRelationshipFromMethod($key, $camelKey);
    }
}

4) In the end Laravel calls getResults() on the relation which then results in a get() on the query builder instance. (And that gives the same result as $model->relation()->get().

How to append property with Accessors in Eloquent

Sometimes, we need to add more property to the Model or transform the original Model. We can define our own custom fields on top of existing in the database table. But, there is an Eloquent property name $appends to help us done this quickly.

I assume our User Model has these fields.

User Table

Since we only have one field to hold the full name. In this case, it names name. But in other contexts, we need to separate the full name down to the first name and the last name. Here $appends property comes to help.

We define the extent fields that we want to return them in $appends property of User Model.

class User extends Model
...
	protected $appends = [
		'first_name',
		'last_name'
	];
...
}

Then, we define the related methods which return its value.

public function getFirstNameAttribute()

The name of the method adhere to this rule: get{camel_case_property_name}Attribute

For example: If our extent field name first_name so the name of method should be getFirstNameAttribute

Finally, we return the new value for new property in this method.

	public function getFirstNameAttribute()
	{
		if ( $this->name === '' ) return '';

		$name = explode( ' ', $this->name );

		return $name[0];
	}

Connection Refused while testing in Laravel

While you are running the test case in Laravel, you may catch this error as image shown below.

The error said your configuration was wrong. So, you need to check it. There is a difference a bit in the Testing environment is the Laravel App is using another configuration file. It used the file name  `.env.testing`. So you need to update that file same with .env file.

How to Set up Docker for Laravel 5

Okay, so today I have a task that I need to run a Laravel App. I decided to set up a Docker for it. So I could share the development environment for my teammate. At this point, I was thinking about a Docker image which helps me handle it. So I researched around and got some Docker Images. After I tried them, I felt they are challenging to use at least for me. So I decided to build a Docker Image to set up the environment for Laravel App. Here we go.

1. First Step – Prepare your Laravel resource

You should follow this article to know how to install Laravel 5. I used Laravel Installer. To check if it installed already by the command `which laravel`. If you got the message like this `.composer/vendor/bin/laravel`, congrats Laravel CLI ready to use. Create your app folder which you will build Docker environment. In my case is `docker-laravel-5` `mkdir docker-laravel-5` > Note: I used OSX Then cd to `docker-laravel-5` folder and type this line in terminal `laravel new app`. This command will pull the initial source and put them all inside the app folder.

2. Second Step – Prepare to Build Docker environment

In `docker-laravel-5` folder, you create a `docker-compose.yml` file. That file contains our Docker configuration. To find out more refer Docker Compose Documentation. `> docker-compose.yml` I also create a `build` and `config`, `data` folders to use later. > The current version of Docker Compose is 3.7. But in my app, I used version 3.0. We will need PHP and Mysql to run a Laravel App. We don’t need a full web server here. Laravel builds a Local Development Server. We only need to boot it. At this time, our `docker-compose.yml` looks like this
version: '3'

services:

  // we will define out services here
We will define 2 services for PHP and Mysql soon.

2.1 PHP-FPM Service

Create a folder named `build`. That folder contains our own Dockerfile. Refer the Dockerfile Document. Then I created a file name `php.dockerfile` to config my PHP service.
FROM php:7.2-fpm

RUN apt-get update && apt-get install -y \
mysql-client libmagickwand-dev --no-install-recommends \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
&& docker-php-ext-install -j$(nproc) iconv \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-install pdo pdo_mysql

RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
&& php composer-setup.php \
&& php -r "unlink('composer-setup.php');" \
&& chmod +x composer.phar \
&& mv composer.phar /usr/local/bin/composer

WORKDIR /var/www/html

ENTRYPOINT ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8181"]

EXPOSE 8181
  Add this to `docker-compose.yml` file.
  phpfpm:
    build:
      context: ./build
      dockerfile: php.dockerfile
    volumes:
      - ./app:/var/www/html
      - ./config/php.ini:/usr/local/etc/php/php.ini
    ports:
      - 8088:8181
    depends_on:
      - mysql
line 3: Define context for build parameter is `build` folder. line 4: Specific name of Dockerfile. line 6: We mounted `app` folder in the host machine to `/var/www/html` folder in the container. line 7: We mounted `php.ini` config file to `/usr/local/etc/php/php.ini` line 8-9: We set the port to listen on `8088` on the host machine to map with `8181` the port in the container. line 9-10: I set PHP service depends on MySQL service which I define later.

2.2 Mysql Service

We will need Mysql to store Laravel App data. So we define Mysql Service in `docker-compose.yml` likes this
  mysql:
    image: mysql:latest
    ports:
      - 3306:3306
    volumes:
      - ./data:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_USER=root
      - MYSQL_DATABASE=spark
      - MYSQL_PASSWORD=root
line 2: We used office image on Docker Store for MySQL. line 3-4: We set the port for Mysql Service is on `3306`. You can change it. line 5-6: We mounted `data` folder in the host machine to `/var/lib/mysql` folder in the container. line 7-11: We define Mysql environment variables for Mysql Image. Ok, that’s it. Now, all we need to do is combine them together.
version: '3'

services:

  phpfpm:
    build:
      context: ./build
      dockerfile: php.dockerfile
    volumes:
      - ./app:/var/www/html
      # - ./config/php.ini:/usr/local/etc/php/php.ini
    ports:
      - 8088:8181
    depends_on:
      - mysql

  mysql:
    image: mysql:latest
    ports:
      - 3306:3306
    volumes:
      - ./data:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_USER=root
      - MYSQL_DATABASE=spark
      - MYSQL_PASSWORD=root
Save and run `docker-compose up –build` to build Laravel App container. Then access `http://localhost:8088` to see your result. Hope this help. Thanks for reading!

How to find your fan page ID

Sometimes you use a third party which asks you input your fan page ID. However, you don’t know how to get it. Let me show you how. It is very easy.

Ok, so I will show you two approaches to get your fan page ID

1. You can get it inside about section of your fan page normally

Go to your fan page and click About in menu item on the left sidebar.

Then scroll down and see the Page ID line value

2. The second way is to use this website to get your fan page ID

https://findmyfbid.com/

Go to https://findmyfbid.com/ website end type your fan page URL in the text box. Press Find numeric ID button and boom! your fan page ID showed up.

Eventually, you use that ID for whatever you want to do. Hope this helps. Thanks for reading.

How to add domain to whitelist of Facebook fan page

To use some products of Facebook such as Customer Chat Plugin, you need to add your domain site into whitelist of your fan page. Through this article, I will show you how to do this. It just has a few steps.

Firstly, you should go to settings fan page.

Next, click on the Messenger platform menu on the left

Finally, scroll down to White-listed domains section. Enter your domain into the text box and save.

Note: your domain should activate SSL before added.