Laravel4 seed specific environment - laravel-4

We're are developing multiple applications based on Laravel 4. These applications run on the same webserver.
The Laravel4 environment detection is based on the hostname which sucks because we have multiple applications on the same machine.
We created a work-around in the detection area so that it will set the environment based on the url.
We run the artisan --env=my_env migrate command when we update the applications DB. The problem is in the seeding, the seeding command doesn't have a env option so it will try to seed the db based on the hostname wich will not be correct.
I'm trying all day to find a solution but I can't find any on the Internet and my attempts to build a new command is just taking too much time and energy.
Does someone knows how to set the environment when seeding?
PS: I run the commands on the server through Grunt and I know the environment -inject it into the command-.

You pointed it very well, Laravel environment guessing sucks the way we use to use it, but you can change that:
This is how I do set my environment flawlessly, so I don't have to deal with hostnames and still don't get my local environment conflict with staging and production.
Create a .environment file in the root of your application and define your environment and add your sensitive information to it:
<?php
return array(
'APPLICATION_ENV' => 'development', /// this is where you will set your environment
'DB_HOST' => 'localhost',
'DB_DATABASE_NAME' => 'laraveldatabase',
'DB_DATABASE_USER' => 'laraveluser',
'DB_DATABASE_PASSWORD' => '!Bassw0rT',
);
Add it to your .gitignore file, so you don't risk having your passwords sent to Github or any other of your servers.
Right before $app->detectEnvironment, in the file bootstrap/start.php, load your .environment file to PHP environment:
foreach(require __DIR__.'/../.environment' as $key => $value)
{
putenv(sprintf('%s=%s', $key, $value));
}
And then you just have to use it:
$env = $app->detectEnvironment(function () {
return getenv('APPLICATION_ENV'); // your environment name is in that file!
});
And it will work everywhere, so you don't need to have separate dirs for development and production anymore:
<?php
return array(
'connections' => array(
'postgresql' => array(
'driver' => 'pgsql',
'host' => getenv('DB_HOST'),
'database' => getenv('DB_DATABASE_NAME'),
'username' => getenv('DB_DATABASE_USER'),
'password' => getenv('DB_DATABASE_PASSWORD'),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
),
),
);
Note that I don't set a fallback:
return getenv('APPLICATION_ENV') ?: 'local';
Because I want it to fail on every server I deploy my app to, to never forget configuring my environment on them.
Then you just have to select the environment in your DatabaseSeeder class:
public function run()
{
if( App::environment() === 'development' )
{
$this->call('UserTableSeeder');
}
}

I thought about the situation some days and got to the conclusion that what we're trying to do isn't the correct way.
The correct way would be that every application has his own config files -with the different envs-. This way the resolve function of Laravel works fine.
The situation now is that we have multiple clients within one application and strore does -clients- configuration files within one application. In this case the hostname resolve will return the one client's -every time the same client- config beacuse the clients applications run on the same machine.
Our solution
We are going to write a deployment script for the different clients so that every client has his own application with their configs only (copy application, copy/overwrite client config into app).
Work-around
The answer of #Antonio Carlos Ribeiro works offcourse but had to much impact on our application. We use the different environments and with this solution we had to use the same user/pass info on all environments or provide a different .environment file.
I wrote an Artisan command to make our deployment work for the moment. This command can seed a database with the configuration of the provided environment (php artisan db:seed_env my_env).
use Illuminate\Console\Command;
use Illuminate\Config\Repository;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class SeedEnvironmentDb extends Command {
/**
* The console command name.
*
* #var string
*/
protected $name = 'db:seed_env';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Seed a database with the configuration of the environment';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function fire()
{
$cmd = $this;
$app = App::make('app');
// force the environment to the given one.
$env = $app->detectEnvironment(function() use ($cmd) {
return $cmd->argument('environment');
});
// create new config with the correct environment and overwrite the current one.
$app->instance('config', $config = new Repository(
$app->getConfigLoader(), $env
));
// trigger the db seed (now with the correct environment)
$this->call('db:seed');
}
/**
* Get the console command arguments.
*
* #return array
*/
protected function getArguments()
{
return array(
array('environment', InputArgument::REQUIRED, 'The environment to seed.'),
);
}
/**
* Get the console command options.
*
* #return array
*/
protected function getOptions()
{
return array();
}
}

Related

How to use redis "memory usage keyname" command with laravel facade redis? we can use all command except memory command

We can use below commands in laravel.
$user = Redis::get('user:profile:'.$id);
$values = Redis::lrange('names', 5, 10);
$values = Redis::command('lrange', ['name', 5, 10]);
but can't use memory usage keyname command with laravel redis facade.
Short answer no you can't execute. Let me explain;
public function createCommand($commandID, array $arguments = array())
{
$commandID = strtoupper($commandID);
if (!isset($this->commands[$commandID])) {
throw new ClientException("Command '$commandID' is not a registered Redis command.");
}
$commandClass = $this->commands[$commandID];
$command = new $commandClass();
$command->setArguments($arguments);
if (isset($this->processor)) {
$this->processor->process($command);
}
return $command;
}
this method is used when you invoke methods and it uses this abstract method to decide whether you can invoke a method or not.
abstract protected function getSupportedCommands();
getSupportedCommands method looks like this;
public function getSupportedCommands()
{
return array(
/* ---------------- Redis 1.2 ---------------- */
/* commands operating on the key space */
'EXISTS' => 'Predis\Command\KeyExists',
'DEL' => 'Predis\Command\KeyDelete',
'TYPE' => 'Predis\Command\KeyType',
'KEYS' => 'Predis\Command\KeyKeys',
'RANDOMKEY' => 'Predis\Command\KeyRandom',
'RENAME' => 'Predis\Command\KeyRename',
'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
'EXPIRE' => 'Predis\Command\KeyExpire',
'EXPIREAT' => 'Predis\Command\KeyExpireAt',
'TTL' => 'Predis\Command\KeyTimeToLive',
'MOVE' => 'Predis\Command\KeyMove',
'SORT' => 'Predis\Command\KeySort',
'DUMP' => 'Predis\Command\KeyDump',
'RESTORE' => 'Predis\Command\KeyRestore',
....
...
..
memory usage command is not available in this method. You may check /vendor/predis/predis/src/Profile/RedisVersion300.php or any other class in Profile folder - it is not defined in there.
The reason is memory usage method is available since Redis version 4.0.0. This package is supporting commands until Redis version 3.0.0 as it can be seen from class names such as RedisVersion240, RedisVersion300 etc.
The command is not defined for that, but you can use eval:
Redis::connection('connection')->eval("return redis.call('memory', 'usage', 'keyname')", 0)
Or if you want to use the keyname as a paramater:
Redis::connection('connection')->eval("return redis.call('memory', 'usage', KEYS[1])", 1, 'keyname');
Redis LUA scripts are executed the same way in laravel

Laravel - Using Values Returned from Middleware Group for Global Variables

Background: I want to use the same code for multiple templated web sites. I want the code to identify the domain being accessed and then be able to set global variables to be used throughout the app.
At first, I created \config\global.php and had the logic in there working as expected:
$webUrl = url()->current();
/**************************************************
* Set Conference name based on URL
**************************************************/
$confId = 0;
$confName = '';
$confAbbrev = '';
if(strpos($webUrl, 'webdomain1') > 0) {
$confName = 'Domain 1 Full Name';
$confAbbrev = 'Dom1';
$confId = 25;
}
elseif(strpos($webUrl, 'webdomain2') >0) {
$confName = 'Domain 2 Full Name';
$confAbbrev = 'Dom2';
$confId = 35;
}
However, I eventually found that the "url()" was causing errors which prevented me from using "php artisan" commands throughout the app. After consulting my professional web developer co-worker, he said using a "global" config file for global variables was not best practice and recommended Middleware instead. He took control of my laptop and went REALLY fast...
In \app\Http\Kernel.php, he added the SetDomainVariables line at the end of $middlewareGroups:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SetDomainVariables::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
He then created a new file: \app\Http\Middleware\SetDomainVariables.php
<?php
namespace App\Http\Middleware;
use Closure;
class SetDomainVariables
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
dd($request->getHttpHost());
return $next($request);
}
}
The results of "$request->getHttpHost()" is what I need... it returns the "foo" of www.foo.com. However, I don't know how to assign this value to a variable and return it via "$next" and then how I can put logic around it to set my global variables that I can reference in a Blade partial, etc.
Thank you!
EDIT: If using the \config\global.php is really the right way to accomplish what I want, could I just comment out the "url()" line whenever I want to do a "php artisan" command?
There are a few different ways to approach this problem, the easiest would be to make use of config -- which allows you to get and set configuration values dynamically.
Create a new file called config/sites.php containing an array for each of your sites, ensuring you start each domain with www and replace any . in the domain with - (because . in a config key will not work as Laravel uses a period to access child values).
return [
'default' => [
'id' => 15,
'name' => 'Default Full Name',
'abbreviation' => 'Def',
],
'www-webdomain1-com' => [
'id' => 25,
'name' => 'Domain 1 Full Name',
'abbreviation' => 'Web1',
],
'www-webdomain2-com' => [
'id' => 35,
'name' => 'Domain 2 Full Name',
'abbreviation' => 'Web2',
],
];
You now have configuration values for each of your sites accessible anywhere in your application, e.g: config('sites.www-webdomain1-com.name').
Add the following to your middleware:
public function handle($request, Closure $next)
{
$host = str_slug(starts_with('www.', $request->getHttpHost()));
$configuration = config("sites.{$host}") ?: config('sites.default');
config(['site' => $configuration]);
return $next($request);
}
You've now set the configuration value for the key site to the contents of the site configuration you set in config/sites.php for the request domain.
Anywhere you need to access the active site's configuration option use config('site.property'), e.g:
Hello, welcome to {{ config('site.name') }}
There are better ways to approach this problem, personally I would create a Site model and then use Route Model Binding however the way I've outlined here is very easy to set up for a beginner and should meet your needs.

URL::forceSchema not working before login

I'm using URL::forceSchema('https') to force SSL on my Laravel 5.3 application. However, it only works after login (even the login page isn't being covered with https).
I'm using TrustedProxy with the following configuration:
'headers' => [
(defined('Illuminate\Http\Request::HEADER_FORWARDED') ? Illuminate\Http\Request::HEADER_FORWARDED : 'forwarded') => null,
\Illuminate\Http\Request::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
\Illuminate\Http\Request::HEADER_CLIENT_HOST => null,
\Illuminate\Http\Request::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
\Illuminate\Http\Request::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
]
I finally got it working by adding the following code to register method of AppServiceProvider.
if (env('APP_ENV') === 'production') {
$this->app['request']->server->set('HTTPS', true);
}
Source: https://laracasts.com/discuss/channels/laravel/force-ssl-secure-routes-in-laravel-52?page=1
Add a config called "force_https" in your config file, and don't forget to add it into your .env.
config/app.php
'force_https' => env('FORCE_HTTPS', false),
Then, in app/Providers/AppServiceProvider.php :
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot(UrlGenerator $url)
{
if (config('app.force_https')) {
$url->forceScheme('https');
}
}
This way, whenever you serve your app behind a reverse proxy where connection between proxy and app is insecure, you can set the FORCE_HTTPS to true, and false if your local dev don't require one.

Cache files permissions fix for Kohana Twig module

How to configure Kohana + Twig module so the Twig will set "writable by all" permissions on all of it's cache directory and it's descendant files?
So, for example, when I run my application through the Apache module (mod_php) and cache file owner is apache (or httpd) user, I will be able to remove cache files (to clean the cache or completely remove whole application) using regular user and ssh access.
I'm able to do it with Kohana's cache, but Twig's cache is created somehow differently.
It's not very easy, but not too complicated either. I have achieved state presented below by trial-and-error method.
Create a class that inherits from Twig_Cache_Filesystem and will be used instead of it. Check this out:
<?php
namespace Application\Twig;
class Cache_Filesystem extends \Twig_Cache_Filesystem
{
public function write($key, $content)
{
$old = umask(0000);
parent::write($key, $content);
umask($old);
}
}
Note, that this class must have it's name unique, so it is a good idea to namespace it. Also, it must be accessible to other code, so consider using composer's autoloading feature.
This is the fix itself, rest of the guide is just the way of implementing it into Kohana+Twig ecosystem.
Copy Twig.php from modules/kohana-twig/classes/Twig.php into your application's directory, i.e. application/classes/Twig.php (thank you Kohana's Cascading Filesystem!)
Modify a bit newly copied file, to let Twig_CacheInterface instance be passed in the config file (application/config/twig.php) instead of just a simple string (specifying to the Twig's cache directory). Take a look of my example:
<?php defined('SYSPATH') or die('No direct script access.');
class Twig extends Kohana_Twig
{
/**
* Initialize the Twig module
*
* #throws Kohana_Exception
* #return bool
*/
public static function init()
{
$path = Kohana::$config->load('twig.environment.cache');
if (is_string($path)) {
return parent::init();
} else if ($path instanceof Twig_CacheInterface) {
return true;
}
throw new Kohana_Exception('Twig cache could not be initialized');
}
}
In configuration file for kohana-twig module, i.e. application/config/twig.php (if not yet copied from module to your application, do it now), define environment.cache key like this:
return array(
'loader' => array(
'extension' => 'twig',
'path' => 'views',
),
'environment' => array(
'auto_reload' => (Kohana::$environment >= Kohana::TESTING),
'autoescape' => true,
'base_template_class' => 'Twig_Template',
// Following line is related to this issue and fix:
'cache' => new \Application\Twig\Cache_Filesystem(APPPATH . 'cache/twig'),
'charset' => 'utf-8',
'optimizations' => - 1,
'strict_variables' => false,
),
'functions' => array(),
'filters' => array(),
'tests' => array(),
}
This works for me. Hopefully it will help someone struggling with similar problem.

Can I import a mysql dump to a laravel migration?

I have a complete database and need to create migration. I guess there must be a way to do it from a dump but not sure. Is there any way automatically or at least easier to do this task?
You can import dumps in Laravel like this:
DB::unprepared(file_get_contents('full/path/to/dump.sql'));
If I were to refactor an existing app, though, I'd take the time to write migrations from scratch, import the dump into different tables (or a different db, if table names are the same) then import the content to the new structure via seeds.
Laravel can't do that, but I think this will help: Laravel migration generator
It generate migrations based on existing tables.
This question is answered already, but recently in a project, the provided answers did not satisfy my needs any longer. It also does not import a whole database dump, but one (large) table. I felt I should share that with you.
The problem was, I wanted to import a quite large table (list of zipcodes) during my artisan:migrate operation. The solution with DB::unprepared($dump) took way to long and I found an alternative which is MUCH faster.
Just export your table as CSV and use the following Code in your migration's up() function.
// i had to str_replace the backslash on windows dev system... but works on linux, too
$filename = str_replace("\\", "/", storage_path('path/in/storage/to/your/file.csv'));
$query = "LOAD DATA LOCAL INFILE '".$filename."' INTO TABLE yourtable
FIELDS TERMINATED BY '\t'
ENCLOSED BY ''
LINES TERMINATED BY '\n'
IGNORE 0 LINES
(col1,col2,...);";
DB::unprepared($query);
Just update the query as you need. And of course, you should make sure, that the table with the cols
'col1', 'col2' etc... exists. I created it just before the importing of the file. with Schema::create()...
If you run into following error message:
PDO::exec(): LOAD DATA LOCAL INFILE forbidden
There is a way you can get rid of this message: Although it's not really documented you can just add an 'options' key to your config/database.php file. For example mine looks like that:
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'options' => array(
PDO::MYSQL_ATTR_LOCAL_INFILE => true,
)
Note: i'm currently using laravel 5 but it should work with laravel 4, too.
I have a complete database and need to create migration. I guess there must be a way to do it from a dump but not sure. Is there any way automatically or at least easier to do this task?
Not automatically, but we run dumps in a migration using DB::unprepared(). You could use file_get_contents to import from a .sql file and thus not have to worry about escaping the entire dump's " marks...
<?php
use Illuminate\Database\Migrations\Migration;
class ImportDump extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
DB::unprepared("YOUR SQL DUMP HERE");
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
}
}
Another alternative is using the PDO directly:
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
$sql_dump = File::get('/path/to/file.sql');
DB::connection()->getPdo()->exec($sql_dump);
I am writing my answer as this might help to someone who is using new laravel 8.
In laravel 8, dumping SQL and run migration using SQL(.dump) file is possible. Please refer below link for more detail.
https://laravel.com/docs/8.x/migrations#squashing-migrations
php artisan schema:dump
// Dump the current database schema and prune all existing migrations...
php artisan schema:dump --prune
schema:dump will create new directory under database > schema and SQL dump will stored there.
After that when you try to migrate first it will run dump file from schema and then any pending migration.
another solution work for me in Laravel 5.2:
DB::unprepared(File::get('full/path/to/dump.sql'));
Simple solution provided by Laravel Article for generating migration file from an existing database table.
Try: https://laravelarticle.com/laravel-migration-generator-online
If you can dump to a CSV:
An alternative for some generic data tables (Countries, States, Postal Codes), not via migrations but via seeders. Although you could do it the same way in a migration file.
In your seeder file:
public function run()
{
$this->insertFromCsvFile('countries', 'path/to/countries.csv');
$this->insertFromCsvFile('states', 'path/to/states.csv');
$this->insertFromCsvFile('postal_codes', 'path/to/postal_codes.csv');
}
private function insertFromCsvFile($tableName, $filePath)
{
if( !file_exists($filePath) ){
echo 'File Not Found: '.$filePath."\r\n";
return;
}
$headers = $rows = [];
$file = fopen( $filePath, 'r' );
while( ( $line = fgetcsv( $file ) ) !== false ){
// The first row should be header values that match column names.
if( empty( $headers ) ){
$headers = explode( ',', implode( ',', $line ) );
continue;
}
$row = array_combine( $headers, $line );
foreach( $row as &$val ) if( $val === 'NULL' ) $val = null;
$rows[] = $row;
// Adjust based on memory constraints.
if( count($rows) === 500 ){
DB::table( $tableName )->insert($rows);
$rows = [];
}
}
fclose( $filePath );
if( count($rows) ) DB::table( $tableName )->insert($rows);
}
Run the seeder: php artisan db:seed --class=GenericTableSeeder
You can create laravel migration and models directly from database using https://github.com/XCMer/larry-four-generator
Execute the following code after installing the package
php artisan larry:fromdb
i recently standing in front of the same problem. i didn't want to install a package specially for that, so i decided to write a little tool to help me and others ;)
Here is the link:
http://laravel.stonelab.ch/sql-seeder-converter/
And here you can comment it, if you have any improvement proposals or questions:
http://www.stonelab.ch/en/sql-to-laravel-seeder-converter/
You can use Raahul/Larryfour Package, A model and migration generator for Laravel 4
Raahul/Larryfour Package
After insallation you can use a command line to create a migration from existed database like this:
php artisan raahul:fromdb --only yourdatabase
And you will find the migration in app/migrations/ folder

Resources