Elequent / Illuminate Laravel Database LIKE operation not working - laravel

I'm using the Illuminate database manager from Laravel, which works pretty good except using the LIKE operation for now.
I have tried those options but got nothing:
function initConnection()
{
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'mysql',
'host' => $this->config['host'],
'database' => $this->config['database-name'],
'username' => $this->config['username'],
'password' => $this->config['password'],
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => ''
]);
$capsule->setAsGlobal();
}
And after initializing I tried:
function searchByName($word)
{
return Capsule::table($table)
->get()
->where('name', 'LIKE', '%'.$word.'%')
->first();
}
echo searchByName('John');
I also tried this option:
Capsule::table('table_name')
->select('SELECT * FROM table_name WHERE table_name.name LIKE "%John%"');
this also failed.
I can't find a documentation for using all the operations in Laravel.

you don't need to define the table in eloquent like this. your model will do that by itself if you follow the name convention or u can set table name in the model by :
class YourModelName extends Model
{
protected $table = 'your_table_name';
}
now query like this :
function searchByName($word) {
return Capsule::where('name', 'LIKE', '%'.$word.'%')->get();
}
this will return multiple value which will match the string.

Related

Query parameter binding issue with illuminate/database and illuminate/queue

I'm using Illuminate\Queue outside of a Laravel app inside an add-on for a CMS. So the only instances of Laravel or Illuminate are these packages that I've required:
"illuminate/queue": "^8.83",
"illuminate/bus": "^8.83",
"illuminate/contracts": "^8.83"
I'm first trying to use the Database for the queue as the default driver since the CMS is database driven, then provide options to SQS etc. I've setup everything so the migrations create my queue tables and everything seems to be wired together when I make the following call to push something to the queue.
/** #var \Illuminate\Queue\QueueManager $queue */
$queue->push('test', ['foo' => 'bar']);
Then it ends in the following error. The parameter bindings are not working or something. It's leaving the ? in the values list.
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"exp_dgq_jobs" ("queue", "attempts", "reserved_at", "available_at", "created_at"' at line 1 (SQL: insert into "exp_dgq_jobs" ("queue", "attempts", "reserved_at", "available_at", "created_at", "payload") values (default, 0, ?, 1674567590, 1674567590, {"uuid":"6bf7a17e-dda3-4fed-903a-8714e5a2d146","displayName":"test","job":"test","maxTries":null,"maxExceptions":null,"failOnTimeout":false,"backoff":null,"timeout":null,"data":{"foo":"bar"}}))
I've step debugged the whole request and it feels like a bug, but then again this is really the first time I've used Laravel or one of it's packages, so maybe I'm missing something? This function explicitly sets reserved_at to null, and the Connection->prepareBindings() method doesn't do anything with the ?, it just leaves it as that value, so the query fails.
protected function buildDatabaseRecord($queue, $payload, $availableAt, $attempts = 0)
{
return ['queue' => $queue, 'attempts' => $attempts, 'reserved_at' => null, 'available_at' => $availableAt, 'created_at' => $this->currentTime(), 'payload' => $payload];
}
What am I missing? Everything just looks right to me an I'm kind of at a loss. I'm making this with PHP 7.4 in mind (for the time being). Maybe I'll try 8.1 to see if that changes anything with the Illuminate packages. Using MySQL 8 too.
Update: potentially relevant screenshot just before the error.
Update 2: I tried PHP 8.1 and latest Laravel 9 packages, didn't make a difference.
For more clarity on how I"m creating my QueueManager:
<?php $queue = new Queue;
$queue->addConnection([
'driver' => 'database',
'table' => ee('db')->dbprefix . 'dgq_jobs',
'queue' => 'default',
'retry_after' => 90,
'after_commit' => false,
]);
$databaseConfig = $provider->make('DatabaseConfig');
$queue->addConnector('database', function () use ($databaseConfig) {
$pdo = new PDO(
sprintf('mysql:host=%s; dbname=%s', $databaseConfig['host'], $databaseConfig['database']),
$databaseConfig['username'],
$databaseConfig['password']
);
$connection = new Connection($pdo);
$connectionResolver = new ConnectionResolver(['default' => $connection]);
$connectionResolver->setDefaultConnection('default');
return new DatabaseConnector($connectionResolver);
});
return $queue->getQueueManager();
I was able to reproduce the error you were seeing. I haven't looked too deeply but I think it may be due to the PDO object not setting up the connection exactly as the Illuminate Queue library expects.
This modification to using the Illuminate\Database library to create the connection solved the issue in my test environment:
$database = new \Illuminate\Database\Capsule\Manager;
$queue = new \Illuminate\Queue\Capsule\Manager;
$database->addConnection([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'db_name',
'username' => 'username',
'password' => '',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
]);
$queue->addConnector('database', function () use ($database) {
$connection = $database->getConnection();
$connectionResolver = new \Illuminate\Database\ConnectionResolver(['default' => $connection]);
$connectionResolver->setDefaultConnection('default');
return new \Illuminate\Queue\Connectors\DatabaseConnector($connectionResolver);
});
$queue->addConnection([
'driver' => 'database',
'table' => 'jobs_table',
'queue' => 'default',
'retry_after' => 90,
'after_commit' => false,
]);
$queue->getQueueManager()->push('SendEmail', ['message' => 'test']);

BlueFeatherGroup eloquent-filemaker connection problem

I'm using https://github.com/BlueFeatherGroup/eloquent-filemaker in a Laravel Project to get data from a FileMaker database.
I use a MySQL as default connection in my project, but I need a second connection to a FileMaker Server.
I created in database.php config file a new conn:
'filemaker' => [
'driver' => 'filemaker',
'host' => 'myfmhost',
'database' => 'FMdatabase',
'username' => 'FMUser',
'password' => 'MFPass',
'prefix' => '',
'version' => 'vLatest',
'protocol' => 'https',
],
I created a model "tabla" as follows:
use BlueFeather\EloquentFileMaker\Database\Eloquent\FMModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class tabla extends FMModel
{
use HasFactory;
protected $connection= 'filemaker';
protected $fillable = [ ];
protected $layout = 'tabla';
}
In my Controller:
$data = tabla::all();
dd($data);
and I get this error:
Argument 1 passed to
BlueFeather\EloquentFileMaker\Database\Query\FMBaseBuilder::__construct()
must be an instance of
BlueFeather\EloquentFileMaker\Services\FileMakerConnection, instance
of Illuminate\Database\MySqlConnection given
But if I do this in my controller (Query Builder aproach):
$data = FM::connection('filemaker')->layout('tabla')->get();
dd($data);
It works ok!
What am I doing wrong? Is it possible to use filemaker conn when using Eloquent if it's not the default project connection ?
Thanks in advance!!

Custom created_at attribute on a pivot Model

Situation: I have a model (let's call it Pivot) who is declared as follows:
class Pivot extends Model
{
public $timestamps = false;
.....
}
It has 5 fields: id, notification_id, device_id, created_at, read_at.
This model is used as the pivot table between Notification and Device:
// Notification.php
public function devices()
{
return $this->belongsToMany(Device::class)
->withPivot(['id','created_at','notification_id','device_id', 'read_at']);
}
public function pivots() {
return $this->hasMany(Pivot::class);
}
And i did the same inside the Device class.
My MySQL has a timezone (Europe\Rome) different then UTC and i can't change it because it is already used by different projects.
PROBLEM: How to setup the created_at field to work properly?
I tried to use default timestamps, it works BUT it will also add the updated_at column and I can't have it (it would add too many redundancy).
So i set public $timestamps = false; and i created the field in the database like that:
$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));
The problem of this one is that, as said earlier, MySQL has a wrong timezone (Europe\Rome), so when I read data from this table the created_at value is shifted by 2 hours from the original. This is a problem when i also set the read_at property, because it creates situations where the notification is read before the creation.
The last idea i had was to set the created_at inside a creating event. I followed this laracast guide but, apparently, if the data is inserted as a pivot table from a sync method, events are not called (they didn't worked for me).
Do you have other ideas? of course there are some ugly hacks that could work (like iterating on the created Pivot and set them manually), but i would like to use a better solution.
EDIT: the solution proposed by #Maraboc worked perfectly. I set the timezone property of MySQL config array in config/database.php:
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
'timezone' => '+00:00',
],
Use model events to set the timestamp when you are creating the model. You can set the field and timezone together.
class Pivot extends Model
{
public $timestamps = false;
protected static function boot()
{
static::creating(function($model) {
$model->create_at = Carbon::now('Europe\Rome');
});
parent::boot();
}
}
Don't forget the parent::boot() or else no traits will get booted on the model.

Transaction doesn't work when instantiating eloquant from a class

I try to use transactions OUTSIDE laravel. it works when I include the db instantiation right in my index file as is:
use Illuminate\Database\Capsule\Manager as Db;
$db = new Db;
$db->addConnection( [
'driver' => Settings::DATABASE_DRIVER,
'host' => Settings::DATABASE_HOST,
'database' => Settings::DATABASE_NAME,
'username' => Settings::DATABASE_USERNAME,
'password' => Settings::DATABASE_PASSWORD,
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => ''
] );
# Make this Capsule instance available globally via static methods... (optional)
$db->setAsGlobal();
I can then elsewhere use code like this:
Db::connection()->beginTransaction();
# Create blog post in the database and return its id
$blogPostRecordId = ( new BlogDbModel() )->create( $_POST );
Db::connection()-> rollBack();
it will correctly work and rollback: no row is created in db.
However, If I get an instance of the db from a class, it won't work:
class DbSql
{
/**
* Use the eloquent query builder and orm. Bypass PDO interface.
* #return Capsule
*/
public function db()
{
$capsule = new Capsule;
$capsule->addConnection( [
'driver' => Settings::DATABASE_DRIVER,
'host' => Settings::DATABASE_HOST,
'database' => Settings::DATABASE_NAME,
'username' => Settings::DATABASE_USERNAME,
'password' => Settings::DATABASE_PASSWORD,
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => ''
] );
# Make this Capsule instance available globally via static methods... (optional)
$capsule->setAsGlobal();
// Setup the Eloquent ORM... (optional; unless you've used setEventDispatcher())
//$capsule->bootEloquent();
return $capsule;
}
}
and then use
( new DbSql() )->db()->getConnection()->beginTransaction();
# Create blog post in the database and return its id
$blogPostRecordId = ( new BlogDbModel() )->create( $_POST );
( new DbSql() )->db()->getConnection()->rollBack();
it simply won't work and the transaction is ignored. Why does getting the db instance from a class instantiation make the process fail ? I would prefer to use an instance as needed.
Solution: the db instance must be exactly the same everywhere the transaction is needed:
$dbInstance = ( new DbSql() )->db();
Then use $dbInstance wherever the transaction is to be followed:
$dbInstance->connection()->beginTransaction();
as well as with any db eloquent operation. So pass the $dbInstance as needed. The good thing is that it can be anywhere in you code: in your models, controllers....
Then finish:
$dbInstance->connection()->commit();
If the system is not able to reach this last line, nothing will be committed to the db.

Laravel: Run migrations on another database

In my app every user has its own database that created when user registered. Connection and database data (database name, username, password) are saved in a table in default database.
try{
DB::transaction(function() {
$website = new Website();
$website->user_id = Auth::get()->id;
$website->save();
$database_name = 'website_'.$website->id;
DB::statement(DB::raw('CREATE DATABASE ' . $database_name));
$websiteDatabase = new WebsiteDatabase();
$websiteDatabase->website_id = $website->id;
$websiteDatabase->database_name = $database_name;
$websiteDatabase->save();
});
} catch(\Exception $e) {
echo $e->getMessage();
}
Now I want to run some migrations on new user's database after its creation.
Is it possible?
thanks
If you place database config on the database.php file, this can help you:
php artisan migrate --database=**otherDatabase**
In your app/config/database.php you have to:
<?php
return array(
'default' => 'mysql',
'connections' => array(
# Our primary database connection
'mysql' => array(
'driver' => 'mysql',
'host' => 'host1',
'database' => 'database1',
'username' => 'user1',
'password' => 'pass1'
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
# Our secondary database connection
'mysql2' => array(
'driver' => 'mysql',
'host' => 'host2',
'database' => 'database2',
'username' => 'user2',
'password' => 'pass2'
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
),
);
Now that you prepared two database connections in your migration you can do:
Schema::connection('mysql2')->create('some_table', function($table)
{
$table->increments('id');
});
This should work.
More infos on: http://fideloper.com/laravel-multiple-database-connections
If you mean using different database connection, it exists in the docs:
Schema::connection('foo')->create('users', function (Blueprint $table) {
$table->bigIncrements('id');
});
I have the same problem, my solution is to change the database using Config::set first then run Artisan::call("migrate"). so based on your code:
DB::statement(DB::raw('CREATE DATABASE ' . $database_name));
Config::set('database.connections.mysql.database', $database_name);
Artisan::call("migrate --database=mysql");
the config only changed on your session then reset later as your current setting.
This is tedious to remember which migration corresponds to which database.
For Laravel 5.5 I used this approach:
public function up()
{
// this line is important
Illuminate\Support\Facades\DB::setDefaultConnection('anotherDatabaseConnection');
Schema::table('product',
function (Blueprint $table)
{
$table->string('public_id', 85)->nullable()->after('ProductID');
});
// this line is important, Probably you need to set this to 'mysql'
Illuminate\Support\Facades\DB::setDefaultConnection('nameOfYourDefaultDatabaseConnection');
}
All migrations can be run automatically without taking care of specifying database manually when running them.
Please note that migrations table is stored inside your default database.
I actually faced the same problem and the answer of Joe did not work in my case, as I have different database connections (so different host, port, user and pass).
Therefore the migration must do a lot of reconnects all the time:
Migration starts with default database (in my case that is client_1)
Fetches stuff from table migrations and clients
Disconnect default database
Connect to database of client_2, run migration parts, disconnect client_2
Connect to default database again, store migration "log"
And then loop it for each client.
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
$defaultConnection = BackendConfig::getDatabaseConfigArray();
$clients = ClientController::returnDatabasesForArtisan();
foreach ($clients as $client) {
BackendConfig::setDatabaseFromClient($client);
Schema::create('newtable', function (Blueprint $table) {
$table->increments('id')->unsigned();
$table->timestamps();
});
BackendConfig::setDatabaseFromArray($defaultConnection);
}
}
And the class where the magic is stored:
class BackendConfig
{
public static function getDatabaseConfigArray($client_id = 1)
{
$connection = config('database.default');
return [
'id' => $client_id,
'host' => config("database.connections.$connection.host"),
'port' => config("database.connections.$connection.port"),
'username' => config("database.connections.$connection.username"),
'password' => config("database.connections.$connection.password"),
];
}
public static function setDatabaseFromArray($array)
{
self::setDatabase($array['id'], $array['host'], $array['port'], $array['username'], $array['password'], true);
DB::disconnect();
}
public static function setDatabaseFromClient(Client $client)
{
DB::disconnect();
self::setDatabase($client->id, $client->database->host, $client->database->port, $client->database->username, $client->database->password, true);
}
public static function setDatabase($client_id, $host, $port, $username, $password)
{
$connection = config('database.default');
$database_name = $connection . '_' . $client_id;
config([
"database.connections.$connection.database" => $database_name,
"database.connections.$connection.host" => $host,
"database.connections.$connection.port" => $port,
"database.connections.$connection.username" => $username,
"database.connections.$connection.password" => $password,
]);
}
With this solution I can run the exact same migrations on every client, yet the migration is just stored in client_1, my sort of master client.
However, pay attention to the two DB::disconnect();. It will screw up the situation without those as then migrations logs are stored in another client's database or such.
Ah and by the way, ClientController does nothing special:
public static function returnDatabasesForArtisan()
{
return Client::select('*')->with('database')->get();
}
I think I finally figured out this mess... This solution doesn't need a configuration for each tenant's database and has to be run only once.
class MigrationBlah extends Migration {
public function up() {
$dbs = DB::connection('tenants')->table('tenants')->get();
foreach ($dbs as $db) {
Schema::table($db->database . '.bodegausuarios', function($table){
$table->foreign('usuario')->references('usuarioid')->on('authusuarios');
});
}
}
}
Where I have a connection named "tenants" on my database.php, which contains the database name of all of my tenants. I have the default connection set to my tenants database as well. That database is the one responsible for taking care of the migrations table.
With the foreach statement, it goes through the tenant databases and runs the migration on each one.
On your default connection, you should configure a user that has access to all tenant's databases for it to work.
Best solution is you can call this method on AppServiceProvide
it is the best solution for this type of problem. I am using this in my project. In my case, I have two environments Development and Production. so when the project is development mode then it will look on local Server else Live server. So you can set dynamic-DB concept here.
you have to make a function then you have to call this inside of boot() Function on App\Providers\AppServiceProvide.php
public function boot()
{
DBConnection();
}
I created Helper File for this. so my code in helper.php
function DBConnection()
{
if( env('APP_ENV') == 'local' )
{ $databse_name = "test_me";
$host = '127.0.0.1';
$user="root";
$password="";
}
else
{
$databse_name = 'tic_tac';
$host = 'localhost';
$user="";
$password="";
}
$state = true;
try {
Config::set('database.connections.myConnection', array(
'driver' => 'mysql',
'host' => $host,
'database' => $databse_name,
'username' => $user,
'password' => $password,
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
));
/* \DB::setDefaultConnection('myConnection');
$state = \DB::connection()->getPdo();*/
Config::set('database.connections.myConnection.database', $databse_name);
\DB::setDefaultConnection('myConnection');
\DB::reconnect('myConnection');
} catch( \Exception $e) {
$state = false;
}
return $state;
}

Resources