Skip to content

Commit

Permalink
Added column casting feature for data insertion
Browse files Browse the repository at this point in the history
  • Loading branch information
glushkovds committed Sep 28, 2023
1 parent 7af0c5b commit 10cd5a9
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 6 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.19.0 [2023-09-28]

### Features
1. Added column casting feature for data insertion

## 1.18.0 [2023-07-10]

### Features
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,32 @@ $rows = MyTable::select(['field_one', new RawColumn('sum(field_two)', 'field_two

## Advanced usage

### Columns casting

Before insertion, the column will be converted to the required data type specified in the field `$casts`.
This feature does not apply to data selection.
The supported cast types are: `boolean`.

```php
namespace App\Models\Clickhouse;

use PhpClickHouseLaravel\BaseModel;

class MyTable extends BaseModel
{
/**
* The columns that should be cast.
*
* @var array
*/
protected $casts = ['some_bool_column' => 'boolean'];
}
// Then you can insert the data like this:
MyTable::insertAssoc([
['some_param' => 1, 'some_bool_column' => false],
]);
```

### Events

Events work just like an [eloquent model events](https://laravel.com/docs/9.x/eloquent#events)
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ services:
soft: 262144
hard: 262144
ports:
- "8124:8123"
- "18123:8123"

clickhouse2:
image: yandex/clickhouse-server
Expand Down
32 changes: 29 additions & 3 deletions src/BaseModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,7 @@ public function save(array $options = []): bool
throw new Exception("Clickhouse does not allow update rows");
}
$this->exists = !static::insertAssoc([$this->getAttributes()])->isError();

$this->fireModelEvent('saved', false);

return $this->exists;
}

Expand All @@ -189,6 +187,17 @@ public static function insert(array $rows): Statement
public static function insertBulk(array $rows, array $columns = []): Statement
{
$instance = new static();
if ($castsAssoc = (new static())->casts) {
$casts = [];
foreach ($castsAssoc as $castColumn => $castType) {
if ($index = array_search($castColumn, $columns)) {
$casts[$index] = $castType;
}
}
foreach ($rows as &$row) {
$row = static::castRow($row, $casts);
}
}
return $instance->getThisClient()->insert($instance->getTableForInserts(), $rows, $columns);
}

Expand Down Expand Up @@ -220,6 +229,11 @@ public static function insertAssoc(array $rows): Statement
$row = array_replace(array_flip($keys), $row);
}
}
if ($casts = (new static())->casts) {
foreach ($rows as &$row) {
$row = static::castRow($row, $casts);
}
}
$instance = new static();
return $instance->getThisClient()->insertAssocBulk($instance->getTableForInserts(), $rows);
}
Expand All @@ -236,7 +250,7 @@ public static function prepareAndInsertAssoc(array $rows): Statement
}

/**
* Prepare row to insert into DB, non associative array
* Prepare row to insert into DB, non-associative array
* Need to overwrite in nested models
* @param array $row
* @param array $columns
Expand All @@ -258,6 +272,18 @@ public static function prepareAssocFromRequest(array $row): array
return $row;
}

protected static function castRow(array $row, array $casts): array
{
foreach ($casts as $index => $castType) {
$value = $row[$index];
if ('boolean' == $castType) {
$value = (int)(bool)$value;
}
$row[$index] = $value;
}
return $row;
}

/**
* @param string|array|RawColumn $select optional = ['*']
* @return Builder
Expand Down
4 changes: 4 additions & 0 deletions tests.bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ cp /src/tests/config/database.php /app/config/database.php
cp /src/tests/config/app.php /app/config/app.php
cp /src/tests/migrations/exampleTable.php /app/database/migrations/2022_01_01_000000_example.php
cp /src/tests/migrations/example2Table.php /app/database/migrations/2022_01_01_000001_example.php
cp /src/tests/migrations/example3Table.php /app/database/migrations/2022_01_01_000002_example.php
cat /src/tests/config/.env >> /app/.env

# Creating test tables
php artisan migrate

# Running tests
php artisan test
35 changes: 35 additions & 0 deletions tests/CastsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Tests;

use Tests\Models\Example3;

class CastsTest extends TestCase
{

protected function setUp(): void
{
parent::setUp();
Example3::truncate();
}

public function testCasts()
{
Example3::insertAssoc([['f_int' => 1, 'f_bool' => false]]);
$rows = Example3::where('f_int', 1)->getRows();
$this->assertEquals(false, $rows[0]['f_bool']);

Example3::truncate();
Example3::insertBulk([[2, false]], ['f_int', 'f_bool']);
$rows = Example3::where('f_int', 2)->getRows();
$this->assertEquals(false, $rows[0]['f_bool']);

Example3::truncate();
$one = new Example3();
$one->f_int = 3;
$one->f_bool = false;
$one->save();
$rows = Example3::where('f_int', 3)->getRows();
$this->assertEquals(false, $rows[0]['f_bool']);
}
}
15 changes: 15 additions & 0 deletions tests/Models/Example3.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Tests\Models;

use PhpClickHouseLaravel\BaseModel;

/**
* @property bool $f_bool
* @property int $f_int
*/
class Example3 extends BaseModel
{
protected $table = 'examples3';
protected $casts = ['f_bool' => 'boolean'];
}
2 changes: 1 addition & 1 deletion tests/migrations/example2Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function up()
{
static::write(
"
CREATE TABLE examples2 (
CREATE TABLE IF NOT EXISTS examples2 (
created_at DateTime64 DEFAULT now64(),
f_int Int64,
f_int2 Int64,
Expand Down
34 changes: 34 additions & 0 deletions tests/migrations/example3Table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

return new class extends \PhpClickHouseLaravel\Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
static::write(
"
CREATE TABLE IF NOT EXISTS examples3 (
created_at DateTime64 DEFAULT now64(),
f_int Int64,
f_string String,
f_bool Bool
)
ENGINE = MergeTree()
ORDER BY (f_int)
"
);
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
static::write('DROP TABLE examples3');
}
};
2 changes: 1 addition & 1 deletion tests/migrations/exampleTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public function up()
{
static::write(
"
CREATE TABLE examples (
CREATE TABLE IF NOT EXISTS examples (
created_at DateTime64 DEFAULT now64(),
f_int Int64,
f_int2 Int64,
Expand Down

0 comments on commit 10cd5a9

Please sign in to comment.