Laravel PHPUnit error mysql fulltext index - laravel

Just got an error while running PHPUnit in Laravel after add FULLTEXT index
Doctrine\DBAL\Driver\PDOException: SQLSTATE[HY000]: General error: 1 near "name": syntax error
After investigating, the error caused by migration that I've added
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddFulltextIndexToProductName extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
DB::statement('ALTER TABLE products ADD FULLTEXT fulltext_index(name)');
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
DB::statement('DROP INDEX `fulltext_index`');
}
}
If I remove that migration code, the test running gracefully
Has anyone experienced this problem?

Usually Laravel tests are using in memory sqlite database and this statement in your migration will not work.
You can check how to create sqlite full text index here: https://www.sqlitetutorial.net/sqlite-full-text-search/
Since Laravel doesn't support full text search out of the box I assume you have written custom function which probably also will not work in tests.
To overcome the issue you can:
use mysql for test (not recommended since it is slow)
use repositories pattern
skip the migration when in tests (if you are not
testing the search - you don't need it). You can check with
if(env('APP_ENV') === 'testing')
use laravel scout for search

Related

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

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?

How to index table column of specific database with code in laravel

I want to write code to index the column of a specific database table column.
Iam trying in this way
DB::connection('mysql2')->raw("ALTER TABLE `consignments` DROP INDEX customer_reference");
What is correct method for this process
You need to use the Laravel migrations, see this documentation:
Laravel Migrations
Laravel Migrations/Index related
You can execute this command in the shell on the project path and edit the created file in the database/migration path and add the index for the column following the documentation
php artisan make:migration add_index_to_consignments_table
Or create manually a file in the database/migrations path with a name like this:
2021_07_29_022532_add_index_to_consignments_table.php and copy/paste this php content
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIndexToConsignmentsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up(): void
{
Schema::table('consignments', function (Blueprint $table) {
//Uncomment one line depending on what you need
//$table->index('customer_reference'); //If you need to add an index
//$table->dropIndex('name_of_index_on_table'); //If you need to remove an index
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down(): void
{
Schema::table('consignments', function (Blueprint $table) {
//Uncomment one line depending on what you need
//$table->dropIndex('name_of_index_on_table'); //If you need to add an index
//$table->index('customer_reference'); //If you need to remove an index
});
}
}
After creating the file with any method you have to execute this command in the shell on the project path:
php artisan migrate

Laravel & PostGres - Migration down() will not drop table (TimeScaleDB Extension)

Here is what create_facts_table looks like:
class CreateFactsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::connection('pgsql')->create('facts', function (Blueprint $table) {
$table->bigInteger('entry_id');
$table->string('tag');
$table->timestampTz('time');
$table->double('sensor_value');
$table->index(['entry_id', 'tag', 'time']);
$table->unique(['entry_id', 'tag', 'time']);
});
DB::connection('pgsql')->statement('SELECT create_hypertable(\'facts\', \'time\');');
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
// Tried both, neither would delete the table
Schema::connection('pgsql')->dropIfExists('facts');
//DB::connection('pgsql')->statement('DROP TABLE facts;');
}
}
I don't get any error from down(). I am able to login as the DB user specified in my .env file and run DROP TABLE facts.
When I run php artisan migration:fresh up() fails to create the table and throws a duplicate table error:
Duplicate table: 7 ERROR: relation "facts" already exists
After manually deleting the table, I can then run php artisan migration:fresh. I must specify connection('pgsql') as I'm using multiple databases. Semi-unrelated, but I'm using TimeScaleDB extension (hence create_hypertable())
I dug a bit deeper on this recently. I hadn't directly mentioned I was using the TimeScaleDB extension ('SELECT create_hypertable(\'facts\', \'time\');').
I updated the title of the question incase someone lands here from Google in the future.
Here is what I have learned:
When php artisan migration:fresh is run, it attempts to drop all tables in batch and does not use the down() method. TimeScaleDB hypertables cannot be deleted in batch.
The solution is to use php artisan migration:refresh instead which will run the defined drop() operations for each table.
Source: https://stackoverflow.com/a/69105447/5449796

Can we create a migration for the data, not just structure of the tables

I love laravel because of the ability to create migrations.
From what I understood, laravel gives us the capability to create a migration where we can repeat the same process without having to create tables and structures manually.
My Question:
Similarly,
1) If I want that my data (inputs to the table) is also stored in some way, so that whenever i change the data in the database, that can also be reverted back or the whole process can also be recreated.
2) If 1 is not possible then can we have a way to save the methods for the "initial" seeding of the database. (so when we "factory" reset the whole thing, it can also automatically populate the content of the database, not just the structure of the database)
Any references for the same please?
I hope I was able to make myself clear!
You're correct in assuming Laravel is incredible! So with respect to your first question.
1) If I want that my data (inputs to the table) are also stored in some way, so that whenever i change the data in the database, that can also be reverted back or the whole process can also be recreated.
If you you want to recreate the data you will need to create a table seeder. To do this, simply create a seeder and a factory with artisan.
php artisan make:seeder UsersTableSeeder
After making your seeder you can run it this with command:
composer dump-autoload && php artisan db:seed
If you wanted to create Controllers, Seeders and Factories at the same time when you make a model type this artisan command.
php artisan make:model User -fa
You can see more on the Laravel documentation for creating seeders and factories here.
Instead of messing with your migration files, I would create seeders. Here is a couple examples.
Exhibit 1 - Example of a an Article Factory (database/factories/ArticleFactory.php)
<?php
use Faker\Generator as Faker;
$factory->define(App\Article::class, function (Faker $faker) {
return [
'title' => $faker->text(50),
'slug' => $faker->unique()->slug,
'body' => $faker->text(200),
'user_id' => rand(1,10),
];
});
Exhibit 2 - Example of an Article Seeder (database/seeds/ArticleTableSeeder.php)
<?php
use Illuminate\Database\Seeder;
class ArticlesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
factory(App\Article::class, 10)->create();
}
}
Exhibit 3 - Example of an Article Migration (database/migrations/2018_05_13_create_articles_table.php)
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateArticlesTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('body');
$table->string('slug');
$table->integer('media_id')->nullable();
$table->integer('user_id')->nullable(); // Owner of Article
$table->timestamps();
$table->softDeletes();
$table->index('slug');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('articles');
}
}
Exhibit 4 - DatabaseTableSeeder.php
<?php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use Faker\Factory as Faker;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* #return void
*/
public function run()
{
// Disable all mass assignment restrictions
Model::unguard();
// Seeds the Articles
$this->call(ArticlesTableSeeder::class);
Then to do a complete factory reset, all you need to do is type the following artisan command:
php artisan migrate:db --fresh
php artisan db:seed
Answer 1) Yes you can manually automatically insert your data into your tables each time you run your migrations.
Answer 2) Question one is possible
Example:
public function up()
{
// Create the table
Schema::create('users', function($table){
$table->increments('id');
$table->string('email', 255);
$table->string('password', 64);
$table->boolean('verified');
$table->string('token', 255);
$table->timestamps();
});
// Insert some stuff
DB::table('users')->insert(
array(
'email' => 'name#domain.com',
'verified' => true
)
);
}
Although you can do data migrations by both using a migration file or a seeder file (as explained in the above answers), from experience I strongly recommend that you put such migration code inside a seeder file, not a migration file.
The reason being is that it's very difficult to run a single file migration. Migrations are designed to run all at once, or to run migrations in step by step sequence since the last migration was done, migrations are not designed to be cherry picked individually, see migration help:
php artisan migrate --help
Usage:
migrate [options]
Options:
--database[=DATABASE] The database connection to use.
--force Force the operation to run when in production.
--path[=PATH] The path of migrations files to be executed.
--pretend Dump the SQL queries that would be run.
--seed Indicates if the seed task should be re-run.
--step Force the migrations to be run so they can be rolled back individually.
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
--env[=ENV] The environment the command should run under
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Help:
Run the database migrations
you will notice that there is no option available to run a handpicked migration, something that you may want to do some day with your data migration (example: suppose you simply want to take data from one legacy table to another table, think of it as moving data from a transactional database to an analytics database using a nightly cron job or something).
This option is however available in a seeder:
php artisan db:seed --help
Usage:
db:seed [options]
Options:
--class[=CLASS] The class name of the root seeder [default:
which makes it much more flexible than a migration (not to mention that seeding data is all about data, which is more appropriate for your task)

Laravel: renaming database table breaks functionality

I'm still quite new to Laravel, Eloquent and Artisan.
What I'm trying to do is pretty easy: I want to create a new Eloquent model AboutUs, along with a migration file to create the table about_us.
I run the following command:
PHP artisan make:model AboutUs -m
This generates the model and migration file, however, the migration file is named '2017_07_18_211959_create_about_uses_table.php', automatically adding the unnecessary 'es' to 'us', and creating a table 'aboutuses' instead of 'about_us'.
If I manually change the migration file like so:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAboutUsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('about_us', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->boolean('active');
$table->string('title')->nullable();
$table->text('text')->nullable();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('about_us');
}
}
The model like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class AboutUs extends Model
{
protected $fillable = ['id', 'active', 'title', 'text'];
public static function getAboutUs()
{
return AboutUs::find(1);
}
public function postAboutUs($session, $active, $title, $text)
{
$aboutUs = $session->get('about_us');
array_push($aboutUs, ['active' => $active, 'title' => $title, 'text' => $text,]);
$session->put('about_us', $aboutUs);
}
}
Then run the migration:
PHP artisan migrate
The database table 'about_us' is created correctly, but when I insert a row in the table and attempt to use getAboutUs, it crashes, the laravel.log stating that:
local.ERROR: exception 'PDOException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'ID226233_db.aboutuses' doesn't exist' in C:\PHP Projects\xxx\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDOConnection.php:77
I can see that there are still references to "aboutuses" in the autoload_classmap and autoload_static files. Changing this manually doesn't fix the issue, nor does running:
composer dump autoload
Next, I tried to simply not rename the table, but run the migration to create the initial "aboutuses" table. This fixed the functionality, as the model now works correctly. However, if I now add a new migration with:
Schema::rename('aboutuses', 'about_us');
This renames the table in the DB, but not in the autoload files or wherever else, resulting in broken functionality.
Surely there must be an easier way to either:
create a model with migration file with a FIXED name, instead of it
automatically changing the name by adding an unnecessary suffix.
rename a model and change the necessary files to prevent the model
from breaking.
Could anyone point me in the right direction before I lose my mind over this? :)
You can specify a custom table name in your Eloquent model class. Here is the example from the docs:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'my_flights';
}
Source: https://laravel.com/docs/5.4/eloquent#eloquent-model-conventions
Hope that helps.

Resources