Home > OS >  Code in Laravel AppServiceProvider halts Composer & build when deploying via DeployHQ
Code in Laravel AppServiceProvider halts Composer & build when deploying via DeployHQ

Time:01-27

I have the following code in my AppServiceProvider.php file:

<?php

namespace App\Providers;

use App\Models\Setting;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {        
        if (Schema::hasTable('settings')) {
            foreach (Setting::all() as $setting) {
                Config::set('settings.'.$setting->key, $setting->value);
            }
        }
    }
}

Which does it's job fine locally, but when I deploy via DeployHQ, it kills the process with the following error:

SQLSTATE[HY000] [2002] No such file or directory (SQL: select * from information_schema.tables where table_schema = giga and table_name = settings and table_type = 'BASE TABLE')

Which kinda makes sense, the database doesn't exist on the build server, so the check cannot run as there's nothing to check. Is there a different way to hydrate a settings config with values from a database on boot which doesn't affect the running of php artisan package:discover?

I know it'll probably be asked, but the .env file etc is all set up correctly. This issue is to do with the fact the build server doesn't have the database, but the server the files get piped to does.

Edit: To give some more context, and perhaps some advice could be given on this, I'm only really using this config value in this code inside a Service class:

public function __construct()
{
    $this->domain = config('api.domain');
    $this->apiVersion = config('api.version');
    $this->bearerToken = config('settings.bearer_token');
    $this->clientId = config('api.client_id');
    $this->clientSecret = config('api.client_secret');
}

Everything online suggests putting these values into the config, however if it's only being called here would it be okay to retrieve it from the Database directly?

CodePudding user response:

The issue is that the code is trying to access the 'settings' table in the database when it doesn't exist on the build server. One way to handle this is to check if the application is running in a production environment before running the code that accesses the database.

You can check the current environment by using the app()->environment() function provided by Laravel.

if (app()->environment() !== 'production') {
    if (Schema::hasTable('settings')) {
        foreach (Setting::all() as $setting) {
            Config::set('settings.'.$setting->key, $setting->value);
        }
    }
}

This will ensure that the code only runs when the application is not in a production environment. This way, the code will not interfere with the running of php artisan package:discover, and will not cause an error on the build server.

You can also move the settings values to a configuration file and include it when the application is running in production, this way the app will not crash when the database is not present.

Another option, if you're only using the settings in this service class, you can retrieve them directly from the database in the constructor method, instead of using the config. This way, you won't have to worry about the settings being available on the build server, and you can still use the values without having to worry about them being present in the config.

public function __construct()
{
    $this->domain = config('api.domain');
    $this->apiVersion = config('api.version');
    $setting = Setting::where('key', 'bearer_token')->first();
    $this->bearerToken = $setting->value;
    $this->clientId = config('api.client_id');
    $this->clientSecret = config('api.client_secret');
}

In this version of the constructor, it retrieves the bearer_token value directly from the settings table in the database, instead of using the config. The api.domain, api.version, api.client_id, and api.client_secret values are still retrieved from the config.

As a reminder, you should make sure that you're handling the case where the setting is not present in the database, to avoid any errors in the application.

  • Related