yii2-redis find() returns empty results - php

I have the following simple model:
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use \yii\redis\ActiveRecord;
use \yii\db\ActiveQuery;
class StatsModel extends ActiveRecord
{
public function attributes()
{
return ['user'];
}
public function rules()
{
return [
['user' : 'string']
];
}
public static function getDb()
{
return \Yii::$app->db_redis;
}
}
\Yii::$app->db_redis; is a config in web.php:
<?php
return [
'class' => 'yii\redis\Connection',
'hostname' => 'localhost',
'port' => 6379,
'database' => 0
];
I do in the redis-cli:
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> hmset key123 user michael
OK
127.0.0.1:6379>
But when i do $test = \app\models\StatsModel::find()->all(); inside layout view i see in the xdebug debugger, that it returns empty set, like there are no records inside redis. I can't understand why, i just added a user and a key to database with index 0. Can someone explain it

ActiveRecord for redis uses dedicated structure and multiple keys to store data about models. Don't expect that you can push some data with random keys and it will be interpreted as active record model. StatsModel::find()->all() does not return anything, because you've not created any model - you've pushed some data to redis, but this is not a active record model. You should use ActiveRecord to save model, if you want to use it with StatsModel::find():
$model = new StatsModel();
$model->id = 1;
$model->user = 'rob006';
$model->save();
$result = StatsModel::find()->all(); // finds one model
Or don't use ActiveRecord and use queries directly:
$result = Yii::$app->db_redis->hget('key123', 'user'); // "michael"

Related

Laravel Mongodb - Can't get the data from the collection

I have an application using Vue + Laravel and I am using mysql database. Now, I need to use mongodb database too.
So, here is my live mongodb database table (projects) and collection (product_1, product_2 etc...)
Like this:
https://prnt.sc/D08akhBur6z4
Now, I want to get all the collection. To do that I have created Model called Import
Import.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Jenssegers\Mongodb\Eloquent\Model;
class Import extends Model
{
protected $connection = 'mongodb';
protected $collection = 'projects';
}
and created a controller called ImportController.php.
ImportController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Models\Import;
class ImportController extends Controller
{
public function show_import () {
$all_import = Import::all();
return response()->json( $all_import, 200);
}
}
.env file
MONGO_DB_HOST=107.200.220.71
MONGO_DB_PORT=57019
MONGO_DB_DATABASE=projects
MONGO_DB_USERNAME=marketplus_pr
MONGO_DB_PASSWORD="my-password"
database.php
'mongodb' => [
'driver' => 'mongodb',
'dsn' => 'mongodb+srv://marketplus_pr:my-password#107.200.220.71/projects?retryWrites=true&w=majority',
'database' => 'projects'
],
Now using this api route call:
http://localhost:3000/api/projects/import/show-import/343-3-3-3-3
I am getting this message:
{"success":false}
But Its should give me all the collection, right?
Can you tell me what I am doing wrong here?
Your request is being intercepted by a different route declaration / controller. (chat reference) Also, wrong collection name.
You should make a few changes in the api.php file:
Move this route declaration:
Route::get('projects/import/show-import/{token}', [App\Http\Controllers\ImportController::class, 'show_import'])->name('show_import');
just after:
Route::group([], function ($router) {
making it as:
Route::group([], function ($router) {
Route::get('projects/import/show-import/{token}',
[App\Http\Controllers\ImportController::class, 'show_import'])-
>name('show_import');
...
}
also, the {token} URL parameter makes not sense so you should remove it.
and, change the collection name to products_1 in the Import.php model file:
class Import extends Model
{
protected $connection = 'mongodb';
protected $collection = 'products_1'; // this should be collection name and not the database name: products_1, products_2, etc.
...
}
By default mongodb runs on port 27017, but looking at your connection string you are not sending that port to the connection. In case you are sure you are running mongodb in that port your connection string needs to be:
mongodb+srv://marketplus_pr:my-password#107.200.220.71:57019?retryWrites=true&w=majority
On the other hand if you are not set the port at the moment of running your mongodb instance, this would be 27017, so the connection string will be:
mongodb+srv://marketplus_pr:my-password#107.200.220.71:27017?retryWrites=true&w=majority

How can I check whether phone number exists in a db during file import?

I want to check if the phone exists or not in the database. If it exists, the row should be skipped. Can I do this without calling the database every time?
namespace App\Imports;
use App\Models\Friend;
use App\Traits\UniqueId;
use Illuminate\Support\Facades\Auth;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class FriendsImport implements ToModel, WithBatchInserts, WithHeadingRow
{
use UniqueId;
public function model(array $row)
{
return new Friend([
'phone' => $row['phone'],
'postcode' => $row['postcode'],
'user_id' => Auth::user()->id,
'unique_id' => $this->generateUniqueNumber(),
]);
}
public function headingRow(): int
{
return 1;
}
public function batchSize(): int
{
return 100;
}
}
First, you can do unique the phones before you insert them into database.
Or, You can use the eloquent to do it.
You can use the FirstOrCreate method.
This method insert a new record when the identity key not found, else return row found.
Can see it: https://laravel.com/docs/8.x/eloquent#retrieving-or-creating-models
You can check like this.
$validator = Illuminate\Support\Facades\Validator::make($row,['phone'=>'require|unique:friends,phone']);
if ($validator->passes()){
// Phone number does not exist on the friends table.
}else{
// Phone number exist on the friends table.
}
If you want to check the existence in the same chunk (batch), you need to save all batch data in static value
like
// Import class
public static $chunkFriends = [];
// in them model function, for each row
array_push (self::chunkFriends, $row);
Then you need to clear $chunkFriends in the each chunk read event.
public function registerEvents(): array
{
return [
ReadChunk::class=> function(){
// put a code here
self::chunkFriends = [];
}
];
}
Another solution is to use Session or Cache driver.

Laravel, change connection in model for one method?

I have got a dev database and a live database. I need to return some results from the live database but only for one method within this model.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class TableName extends Model
{
protected $table = 'table_name';
protected $connection = 'dev';
public $timestamps = false;
public static function live($index) {
$liveId = Settings::where('index', $index)->get()[0];
$live = new TableName;
$live->setConnection('live');
$data = $live::where('index', $liveId->live_index)->get();
dd($live);
return $data;
}
}
If I dd() the $live variable after calling setConnection then it does say that the connection is indeed live. However as soon as I dd() the $data I get the rows from the dev database!
Eloquent provides a nice way to handle multiple connections.
You should just be able to use the on method. For example.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class TableName extends Model
{
protected $table = 'table_name';
protected $connection = 'dev';
public $timestamps = false;
public static function live($index) {
$liveId = Settings::where('index', $index)->get()[0];
$data = self::on('live')->where('index', $liveId->live_index)->get();
return $data;
}
}
That should then run the query using the live connection in your database configuration.
I have personally haven't done anything like this, but I found out way to do this by following these steps.
In the .env file add these new env variables =>
DB_CONNECTION_2=mysql
DB_HOST_2=127.0.0.1
DB_PORT_2=3306
DB_DATABASE_2=database2
DB_USERNAME_2=root
DB_PASSWORD_2=secret
Now inside the config/database.php file specify the 2nd mysql connection with the previously entered env variables.
'mysql2' => [
'driver' => env('DB_CONNECTION_2'),
'host' => env('DB_HOST_2'),
'port' => env('DB_PORT_2'),
'database' => env('DB_DATABASE_2'),
'username' => env('DB_USERNAME_2'),
'password' => env('DB_PASSWORD_2'),
],
Now you can create a Model for the required table =>
class myModel extends Eloquent {
protected $connection = 'mysql2';
}
Then you can use it as the regular way will all the Eloquent features in controller methods =>
$newMy = new myModel;
$newMy->setConnection('mysql2');
$newMy = $someModel->find(1);
return $something;
Here is the doc link that you can read about this more.
You can try to get the default connection before the point with
$defaultConnection = DB::getDefaultConnection();
then change the default connection to before obtaining the results from 'live'
DB::setDefaultConnection('live');
and then restore the connection as soon as 'live' connection is no longer needed
DB::setDefaultConnection($defaultConnection);
As an alternative you can generate your data using DB::connection('live'). More info at Using Multiple Database Connections

How to return a selection in a laravel 5.5 api using a resource

I want to return a selection from my database whenever a user specifies an id.
I have a laravel 5.5 API with in the api.php the following route.
Route::get('companytasks/{id}','TaskController#showOfCompany');
In my TaskController i have the following function:
public function showOfCompany($id)
{
// Get tasks with the specifiec company id
$companytasks = Task::findOrFail($id);
// Return single task as a resource
return new TaskResource($companytasks);
}
And my resource looks like this:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Task extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'TaskCompanyId' => $this->TaskCompanyId,
'TaskName' => $this->TaskName,
'TaskDescription' => $this->TaskDescription,
'Branche' => $this->Branche,
'Budget' => $this->Budget,
'TaskFinishDate' => $this->TaskFinishDate,
'PaydFor' => $this->PaydFor,
'PublicTask' => $this->PublicTask,
'created_at' => $this->created_at
];
}
}
I want to make a selection from the database where the 'TaskCompanyId' equals the $id.
I can do this with the following line in my controller:
$companytasks = Task::where('TaskCompanyId','=',$id)->get();
But this does not work for me, I can however add ->first() to it so it only returns the first occurrance but I want all ocurrances to be returned. How do I achieve this?
You are using 'CompanyTaskId' instead of 'TaskCompanyId' in where clause
Look OK but try with this.
$companytasks = App\Task::where( 'TaskCompanyId' , $id )->get();
If you want you can order or paginate
$companytasks = App\Task::where( 'TaskCompanyId' , $id )
->orderBy('CompanyTaskId', 'desc')
->paginate(15);
I managed to fix it by changing the resource code to:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceCollection;
class Task extends ResourceCollection
{
public function toArray($request)
{
return [
'data' => $this->collection
];
}
}
Making a selection returns a collection instead of an object and therefore needs a ResourceCollection instead of a JsonResource.

Laravel relationship DB connection

I m trying to get Model relationship with different connection, basically my connections are dynamic.
$con = 'con1'
$persons = new \App\Models\Person;
$persons->setConnection($con);
$persons = $persons->where('somevalue', 1)->get()
So here I get Person from con1 (where con1 is stored in config/databse.php
It can be con2, con3, con4 and etc.
However this works, but when I am trying to loop through this data and get relationship it switches to default Database connection.
#foreach($persons as $person)
{{$person->data->name}}
#endforeach
In above loop data is a belongsTo relation ship in Person Model, and it throws error because it switches back to default Database connection and not using con1
It is possible to setConnection() and keep for hasMany relationship and also for belongsTo relationship?
I cant set protected $connection; in Model because connections are changble
What I tried until now is I created an absctract class that extendseloquent, and my models extending this abstract class
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model as Eloquent;
abstract class Database extends Eloquent {
protected $connection;
}
<?php
namespace App\Models;
use App\Models\Database as Database;
class Person extends Database{
protected $table = 'persons';
}
So theoritically when I setConnection I set connection to all models that extends this class but still no luck.
You may try eager loading of the relationship:
$persons = $persons->with('data')->where('somevalue', 1)->get();
I found a solution maybe wrong but is working:
I create a file in config where I add all my connections:
<?php
return [
'con1' => [
'name' => 'Persons Connection',
'url' => 'persons',
'active' = true
],
'con2' => [
'name' => 'Italy Persons Connection',
'url' => 'italypersons',
'active' = false
],
];
So like this I can event control if con2 is available
In Database connection config file I have con1 connection with all db data
After in routes.php a bind the url example /info/{con} where con = 'persons'
Route::bind('con', function($con, $route){
foreach(config('connections') as $key => $value){
if($value['url'] == $con && $value['active'] == true){
session(['connection' => $key]); //where $key == con1
return $key;
}
abort(404);
}
});
So: I have a Class Person that extends abstract method with __construct function
<?php
namespace App\Models;
use App\Models\Database as Database;
class Person extends Eloquent{
protected $table = 'persons';
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model as Eloquent;
abstract class Database extends Eloquent {
function __construct(array $attributes = array())
{
parent::__construct($attributes);
$this->setConnection(session('connection'));
}
}
So like this I am sure if user get to URL /info/{con} It will set the connection sessions if it exist in my connections config file, without losing sensitive data from config/database.php
If somebody have better idea please write it

Categories