Laravel Eloquent supporting MariaDb dynamic column - mysql

For Dynamic column supported in Maria-DB and in MySQL we have JSON column type. For one of our projects, we should be implementing a database for Maria-DB (not Mysql).
The Dynamic Column is supported using yii2-dynamic-ar package.
how can can override Eloquent orm in Laravel to add dynamic-columns. in the Yii package which added this feature to ActiveRecord this classes can override ActiveRecord class
implementations classes in Yii framework to support in ActiveRecord ORM:
DynamicActiveRecord.php
DynamicActiveQuery.php

I just created package for handling MariaDB dynamic Column using eloquent and query builder.
To install package run this command:
composer require halalsoft/laravel-dynamic-column
You can start using the package by adding the HasDynamicColumn trait and use Dynamic as attribute cast to your models.
An example:
use Illuminate\Database\Eloquent\Model;
use Halalsoft\LaravelDynamicColumn\Dynamic;
use Halalsoft\LaravelDynamicColumn\HasDynamicColumn;
class MyModel extends Model
{
use HasDynamicColumn;
protected $casts
= [
'the_column' => Dynamic::class,
];
}
Now You can use dynamic column like json column using eloquent or query builder:
$modelData = MyModel::find(1);
$columnData = $modelData->the_column;
$columnData['data1'] = 'value';
$columnData['data2'] = 'value2';
$modelData->the_column = $columnData;
$modelData->save();
You can also create data field as array
$newData = MyModel::create([
'other_column' => 'this just another column data',
'the_column' => ['data1'=>'value1','data2'=>'value2']
]);
to update a json field/key you use, you may use the -> operator when calling the update method:
$page->update(['content->data1' => 'value1new']);
or you can still update whole column using normal array:
$page->update(['content' => ['data1'=>'value1new','data2'=>'value2new']]);
You can set as array using other method like updateOrCreate(), firstOrCreate(), etc.
This package also support query builder using:
Model::query()->where('the_column->data1', 'value1')->first();
This package is still new, if any issue or request just go to github issue

You can have a cast defined for the column in the Model class
//Model class
protected $casts = ['my_column' => 'array];
You can define the datatype for the column as text if you want or json, with the cast defined, you will be able to work with the column data as associative array.
There's also a package to add json datatype in migration for mariadb - it may be of help
https://github.com/ybr-nx/laravel-mariadb

Related

How to map database function to Entity Framework Core property

my problem is that i can't map database function to use it in object property like computed column but for different tables with relation. My relation is SizeItem to Review (one to many)
I have IEntityTypeConfiguration
internal class SizeItemConfiguration : IEntityTypeConfiguration<SizeItem>
{
public const string TableName = "size_items";
public void Configure(EntityTypeBuilder<SizeItem> builder)
{
builder.ToTable(TableName);
builder.Property(p => p.Id)
.UseHiLo("size_item_hilo");
}
}
And want to compute an average rating from Review entities related to a SizeItem.
I tried to find examples to use database functions and EF Core functions but nothing works.
How can I get computed SizeItem.AverageRating for every sql command? May be code will looks like:
builder.Property(p => Functions.Average(p.Reviews.Select(x => x.Rating)));
User-defined function mapping doesn't work for me because I'm using Repository pattern.

how to name database column appropriately

In excel file, I have columns like this; 11.01-12.00, 12.01-13.00, ..., 29.01-30.00. And the content of the column is the price. I want to store a price in many ranges in MySQL like excel column.
Do I have to create a table like this or Do your guys have any better way please guide me?
An option may be to define a column price_range or prices as json column and store the data as key=>value pairs for eg: '11.01-12:00'=> 11.50
In your migration file for the table, add a column
$table->json('prices');
And in your model class, specify to cast prices column as array.
class MyModel extends Model
{
protected $casts = ['prices' => 'array'];
//... rest of the class code
}
By specifying the cast, you can now use associative array to store data on the column, you can work in php arrays to store and update data.
$myModel = new MyModel;
$myModel->prices = [
'11.01-12.00' => 11.50,
'12.01-13.00' => 12.35,
//...
'29.01-30.00' => 29.82,
];
$myModel->save();
To update data
$myModel = MyModel::find(5);
$newPrices = [
'11.01-12.00' => 11.20,
'12.01-13.00' => 12.63,
//...
'29.01-30.00' => 29.05,
]
$myModel->update(['prices' => $newPrices]);
/OR to update price for a specific price range
$myModel->update(['prices->11.01-12.00' => 11.25]);
Laravel docs:
https://laravel.com/docs/8.x/eloquent-mutators#array-and-json-casting
https://laravel.com/docs/8.x/eloquent#mass-assignment-json-columns
https://laravel.com/docs/8.x/queries#updating-json-columns
In your database table migration file, define a column called price to hold your value.
$table->integer('price');
Store your price as the lowest denominator for your currency performing whatever arithmetic is required to perform the conversions in/out of the database.
There is no need for you to create individual columns for different prices.
Edit 1
Based on your question update that the information is in an excel file, you'll want to make use of a package such as Laravel Excel (no point reinventing the wheel) to perform your import.
Nice tutorial for importing an excel file here

Is it possible to define virtual fields (SQL function calls) in entities?

Is their way, we can add MySQL custom function in Entity
protected $_virtual = ['check_tenant' => '(check_tenant(Tenants.id))'];
I would like to call following query in with find() method
//SELECT id, first_name, check_tenant(Tenants.id) FROM tenants AS Tenants
$this->Tenants->find()->all();
If I can define custom MySQL function in the virtual field then it would automatically return in the result set
I am able to pass the new field in select() method
$this->Tenants->find()
->select(['id', 'check_tenant' => '(check_tenant(Tenants.id))'])->all();
But i would like to define globally, so the new field don't need to pass in every find call
Virtual properties in CakePHP 3.x are not the same as virtual fields in CakePHP 2.x, the latter were used in SQL queries, and the former are being used on PHP level, usually with data already present in the entity.
If you want your custom field to be present in all queries, then you could for example use the Model.beforeFind() event to modify the queries accordingly:
// in TenantsTable class
public function beforeFind(\Cake\Event\Event $event, \Cake\ORM\Query $query, array $options)
{
return $query
// select custom fields (functions builder usage not required, but advised)
->select(function (\Cake\ORM\Query $query) {
return ['check_tenant' => $query->func()->check_tenant([
'Tenants.id' => 'identifier'
])];
})
// ensure that the tables default fields are being selected too
->enableAutoFields(true); // autoFields() before CakePHP 3.4
}
Another less invasive option would be custom finders, that you explicitly use where you need them:
// in TenantsTable class
public function findWithTenantCheck(\Cake\ORM\Query $query, array $options)
{
return $query
->select(/* ... */)
->enableAutoFields(true);
}
// query data
$query = $this->Tenants->find('withTenantCheck');
See also
Cookbook > Database Access & ORM > Table Objects > Lifecycle Callbacks > beforeFind
Cookbook > Database Access & ORM > Entities > Creating Virtual Properties
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Using Finders to Load Data
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Custom Finder Methods
Cookbook > Database Access & ORM > Query Builder > Using SQL Functions

Use MYSQL AES_DECRYPT function in eloquent?

I am using Slim Framework with eloquent ORM for database operations, I want to store encrypted data in the database(MYSQL) for fewer columns of particular tables.
I have tried with AES_ENCRYPTin eloquent model which stored the data well with mutators like,
class MyDataModel extends Model {
public function setProcessDataAttribute($value)
{
$this->attributes['process_data'] =
DB::raw("AES_ENCRYPT('".$value."', '12345')");
}
}
Now, I want to decrypt with the MYSQL function AES_DECRYPT, is there any way to use MYSQL functions in the eloquent model?
I am not sure how can I override the find, get methods in my model.
In laravel 5, recommend use his AES encryption:
$newEncrypter = new \Illuminate\Encryption\Encrypter('your key or app_key#env', config('app.cipher') );
$decrypted = $newEncrypter->decrypt('your encrypted data');

CakePhp mysql raw query error

I'm new to cakephp. I'm trying to search through mysql tables. I want to use nested query.
class TableController extends AppController{
.
.
public function show(){
$this->set('discouns', $this->DiscounsController->query("SELECT * FROM discoun as Discoun WHERE gcil_id = 1"));//(SELECT id FROM gcils WHERE genre = 'Shoes' AND company_name = 'Adidas')"));
}
}
Error:
Error: Call to a member function query() on a non-object
I've also tried
public function show(){
$this->DiscounsController->query("SELECT * FROM count as Count WHERE ctr_id = (SELECT id FROM ctrs WHERE genre = 'Shoes' AND company_name = 'Adidas')");
}
Error:
Error: Call to a member function query() on a non-object
File: C:\xampp\htdocs\cakephppro\myapp\Controller\CountsController.php
Please help. I've been trying this for last few hours. :/
As mentioned in the comments there are a few problems with your code.
Firstly, you are trying to call the query() method on a Controller, whereas you should be executing it on a Model, as it is models that handle database queries and the controller should simply be used to call these methods to get the data and pass them to the view.
The second thing is that you are executing a very simple SQL query raw instead of using CakePHPs built in functions <- Be sure to read this page in full.
Now for your problem, as long as you have setup your model relationships correctly and followed the correct naming conventions, this should be your code to run your SQL query from that controller:
public function show(){
$this->set('discouns', $this->Discouns->find('all', array(
'conditions' => array(
'gcil_id' => 1,
'genre' => 'shoes',
'company_name' => 'Adidas'
)
));
}
query() is not a Controller, but a Model method. That's what the error (Call to a member function on a non-object) is trying to tell you.
So the correct call would be:
$this->Discount->query()
But you are calling this in a TableController, so unless Table and Discount have some type of relationship, you won't be able to call query().
If the Table does have a relationship defined you will be able to call:
$this->Table->Discount->query()
Please not that query() is only used when performing complex SQL queries in scenarios where the standard methods (find, save, delete, etc.) are less practical.
$this->Counts->find('all',array(
'conditions' => array(
'ctrs.genre' => 'Shoes',
'ctrs.company_name' => 'Adidas'
), 'recursive' => 1
));
The above is with tables named counts and ctrs.
This is assuming you have the model set up to have some sort of relationship between the counts table and the ctrs table. It's kind of hard to tell in your code exactly what you tables are.
The CakePHP book should have all the answers you need. One of the reasons to run CakePHP over regular PHP is the FIND statement. Once you have your models set up correctly, using the find statement should be really easy.
http://book.cakephp.org/2.0/en/models.html