codeigniter 4 migration file not creating table - codeigniter

when I first made a migration file for table users, the public function down() in the migration file was empty. when I run php spark migrate the table users was created.
then I generated another migration file with php spark make:migration users, made a few adjustments according to the new table structure and put $this->forge->dropTable('users'); in the public function down(). but when I run php spark migrate again, the users table doesn't have the new field..
I'm using codeigniter 4 and mysql. here's my code
UserModelphp
<?php
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $DBGroup = 'default';
protected $table = 'users';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $insertID = 0;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
// added created_at and updated_at
protected $allowedFields = ['username', 'password', 'foto', 'nama', 'email', 'telepon', 'created_at', 'updated_at'];
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}
first migration file
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class Users extends Migration
{
public function up()
{
// tabel users
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 7,
'auto_increment' => true,
],
'username' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => false,
],
'password' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => false,
],
'profile_pic' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
'nama' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
'email' => [
'type' => 'VARCHAR',
'constraint' => 100,
],
'telepon' => [
'type' => 'VARCHAR',
'constraint' => 10,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('users');
}
public function down()
{
// hapus tabel users
}
}
new migration file
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class Users extends Migration
{
public function up()
{
// tabel users
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 7,
'auto_increment' => true,
],
'username' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => false,
],
'password' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => false,
],
'foto' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
'nama' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
'email' => [
'type' => 'VARCHAR',
'constraint' => 100,
],
'telepon' => [
'type' => 'VARCHAR',
'constraint' => 10,
],
'created_at DATETIME DEFAULT CURRENT_TIMESTAMP',
'updated_at DATETIME DEFAULT CURRENT_TIMESTAMP',
]);
$this->forge->addKey('id', true);
$this->forge->createTable('users');
}
public function down()
{
// hapus tabel users
$this->forge->dropTable('users');
}
}
can someone tell me what I'm doing wrong? any help is appreciated

Explanation:
The down() method isn't called when you execute php spark migrate.
The down() method is run when you perform a migration rollback process using php spark migrate:rollback.
Solution:
Add the $this->forge->dropTable('users'); line of code at the beginning of the up() method of the "new migration file".
new migration file
// ...
class Users extends Migration
{
public function up()
{
$this->forge->dropTable('users');
// ...
}
// ....
}
The purpose of the down() method is to "reverse" everything performed in the up() method.
Extra Notes:
Considering that in your new migration, you're only renaming an existing table column (profile_pic -> foto) and adding timestamp columns, it would make more sense if you specified a more meaningful "migration name".
In addition, instead of dropping & recreating the existing table, modify the table instead.
I.e:
new migration file
A. Command (Create the new migration):
php spark make:migration alter_users_rename_profile_pic_add_timestamps
B. Generated migration.
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AlterUsersRenameProfilePicAddTimestamps extends Migration
{
private $tableName = "users";
public function up()
{
$this->forge->modifyColumn($this->tableName, [
"profile_pic" => [
'name' => 'foto',
'type' => 'VARCHAR',
'constraint' => 50,
]
]);
$this->forge->addColumn($this->tableName, [
'created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP',
'updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
]);
}
public function down()
{
$this->forge->modifyColumn($this->tableName, [
"foto" => [
'name' => 'profile_pic',
'type' => 'VARCHAR',
'constraint' => 50,
]
]);
$this->forge->dropColumn($this->tableName, ["created_at", "updated_at"]);
}
}

Related

how to update key/value database with laravel?

I'm just learning laravel. I want update key / value in database with laravel api but not work.
My products model is one to many with ProductMeta and many to many with contents model.
My Models
class Product extends Model
{
use HasFactory;
protected $guarded = [];
public function productMeta()
{
return $this->hasMany(ProductMeta::class);
}
public function content()
{
return $this->belongsToMany(Content::class, 'product_contents')->withTimestamps();
}
}
class ProductMeta extends Model
{
use HasFactory;
protected $guarded = [];
public function products()
{
return $this->belongsTo(Product::class);
}
}
class Content extends Model
{
use HasFactory;
protected $guarded= [];
public function product()
{
return $this->belongsToMany(Product::class, 'product_contents');
}
Controller
public function update(Request $request, $id)
{
$product = Product::findOrFail($id);
DB::table('product_metas')
->upsert(
[
[
'product_id' => $product->id,
'key' => 'name',
'value' => $request->name,
],
[
'product_id' => $product->id,
'key' => 'price',
'value' => $request->name,
],
[
'product_id' => $product->id,
'key' => 'amount',
'value' => $request->name,
],
],
['product_id','key'],
['value']
);
return \response()->json([], 204);
}
Table Structure
API parameter
I tried with update and updateOrcreate and updateOrInsert and upsert methods.
just in upsert method writed database but inserted new data.not updated.
In your case, you should use updateOrCreate() instead of upsert.
Product::updateOrCreate([
'product_id' => $id,
'name' => $request->name,
'price' => $request->price,
'amount' => $request->amount
]);
or
Product::upsert([
[
'product_id' => $id,
'name' => $request->name,
'price' => $request->price,
'amount' => $request->amount
]
], ['product_id'], ['name', 'price', 'amount']);
In addition your problem is your table name is not matching with your structure table name. In your controller DB::table('product_metas') should be DB::table('products_meta').
my problem solved this way:
ProductMeta::query()->where('product_id', $id)->upsert([
['product_id' => $id, 'key' => 'name', 'value' => $request->name],
['product_id' => $id, 'key' => 'price', 'value' => $request->price],
['product_id' => $id, 'key' => 'amount', 'value' => $request->amount]],
['product_id'], ['value']);
$contentRecord = Product::find($id);
$contentRecord->content()->update(['path'=>$request->path]);
return response()->json([], 204);
I forget use query() method for ProductMeta and added $table->unique(['product_id', 'key']); to product meta migration.
**products relation one to many with product Meta
And Many to many with content.

Codeigniter 4 - Store validation rules in separate files

I have all my rules in the Validation config file, like the documentation suggest:
https://codeigniter4.github.io/userguide/libraries/validation.html#saving-sets-of-validation-rules-to-the-config-file
For example:
public $userCreate = [
'first_name' => [
'label' => 'First Name',
'rules' => 'required|string|max_length[60]'
],
'last_name' => [
'label' => 'Last Name',
'rules' => 'required|string|max_length[60]',
],
'email' => [
'label' => 'Auth.email',
'rules' => 'required|max_length[254]|valid_email|is_unique[users.email]',
],
];
In my controllers I can access my validation groups like this:
$validation = \Config\Services::validation();
$rules = $validation->getRuleGroup('userCreate');
As my app gets bigger, I need more and more validation rules, so the question is, is there a way to organize them in separate files and not to have all of them in a single config file? Something like the custom rules, which are loaded in the config file and stored separately.
Steps
Create a custom directory for storing your validation rules. I.e app/Validation.
Create a class under that directory for your 'User' rules. I.e: app/Validation/UserRules.php
<?php
namespace App\Validation;
class UserRules
{
public function create()
{
return [
'first_name' => [
'label' => 'First Name',
'rules' => 'required|string|max_length[60]'
],
'last_name' => [
'label' => 'Last Name',
'rules' => 'required|string|max_length[60]',
],
'email' => [
'label' => 'Auth.email',
'rules' => 'required|max_length[254]|valid_email|is_unique[users.email]',
],
];
}
public function update()
{
return [
// Add 'User' update rules here.
];
}
}
In the \Config\Validation config file, set the relevant 'User' validation rules in the constructor. I.e:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
// ...
class Validation extends BaseConfig
{
// --------------------------------------------------------------------
// Setup
// --------------------------------------------------------------------
public $userCreate = [];
public $userUpdate = [];
public function __construct()
{
$this->userCreate = ($userRules = new \App\Validation\UserRules())->create();
$this->userUpdate = $userRules->update();
}
// ...
}
In your Controllers, you may access validation groups as usual.
Thanks to #steven7mwesigwa I came up with a solution that suits me the most.
First I created separate classes inside the App/Validation folder. For example these 2 classes:
App\Validation\Auth.php
<?php
namespace App\Validation;
class Auth {
public $login = [
'email' => [
'label' => 'E-mail',
'rules' => 'required|max_length[254]|valid_email',
],
'password' => [
'label' => 'Password',
'rules' => 'required',
],
'remember' => [
'label' => 'Remember me',
'rules' => 'if_exist|permit_empty|integer',
]
];
}
App\Validation\User.php
<?php
namespace App\Validation;
class User {
public $userCreate = [
'first_name' => [
'label' => 'First Name',
'rules' => 'required|string|max_length[60]',
],
'last_name' => [
'label' => 'Last Name',
'rules' => 'required|string|max_length[60]',
],
'email' => [
'label' => 'E-mail',
'rules' => 'required|max_length[254]|valid_email|is_unique[users.email]',
],
];
}
The next step is to add a construct method to the existing validation config file:
App\Config\Validation.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Validation extends BaseConfig {
...
// --------------------------------------------------------------------
// Rules
// --------------------------------------------------------------------
public function __construct() {
$ruleGroups = [
new \App\Validation\Auth(),
new \App\Validation\User(),
];
foreach ($ruleGroups as $ruleGroupClass) {
foreach ((array) $ruleGroupClass as $groupName => $rules) {
$this->{$groupName} = $rules;
}
}
}
}

Laravel-Auditing is not working without any errors

I've recently installed this package and configured everything with guide but some how it's not working!
By it's not working I mean it's not adding anything to database. I really don't know what is wrong with my configs but I've checked everything with guide 3 times and everything is correct but... I don't know
config/audit.php:
<?php
return [
'enabled' => env('AUDITING_ENABLED', true),
'implementation' => OwenIt\Auditing\Models\Audit::class,
'user' => [
'morph_prefix' => 'user',
'guards' => [
'web',
'api',
],
],
'resolver' => [
'user' => OwenIt\Auditing\Resolvers\UserResolver::class,
'ip_address' => OwenIt\Auditing\Resolvers\IpAddressResolver::class,
'user_agent' => OwenIt\Auditing\Resolvers\UserAgentResolver::class,
'url' => OwenIt\Auditing\Resolvers\UrlResolver::class,
],
'events' => [
'created',
'updated',
'deleted',
'restored',
'gold_mailed' => 'goldMailed',
'invited' => 'clientInvited',
],
'strict' => false,
'timestamps' => false,
'threshold' => 0,
'driver' => 'session',
'drivers' => [
'eloquent' => [
'table' => 'audits',
'connection' => null,
],
],
'console' => true,
];
My model that I want to audit:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use OwenIt\Auditing\Contracts\Auditable;
use App\Models\Expansion;
use App\Models\Audit;
class Setting extends Model implements Auditable
{
protected $table = 'settings';
use \OwenIt\Auditing\Auditable;
protected $fillable = [
'expansion_id', 'season', 'advertiser_app', 'pvp_app', 'raid_app', 'version'
];
protected $auditInclude = [
'expansion_id', 'season', 'advertiser_app', 'pvp_app', 'raid_app', 'version'
];
public function Expansion()
{
return $this->hasOne(Expansion::class, 'id', 'expansion_id');
}
}
web.php:
Route::post('/setting' , 'Admin\SuperAdminController#saveSetting')->middleware('superadmin')->name('admin_save_setting');
Controller:
public function saveSetting(Request $request)
{
$sql = Setting::where('id', 1)->update([
'expansion_id' => $request['expansion_id'],
'season' => $request['season'],
'advertiser_app' => $request['advertiser_app'],
'pvp_app' => $request['pvp_app'],
'raid_app' => $request['raid_app'],
'version' => $request['version']
]);
if ($sql) {
toastr()->success('Settings successfully updated.');
return redirect()->back();
}
toastr()->error('Something went wrong!');
return redirect()->back();
}
I don't know what infos do you need but I think this is enough
I think my problem is with "driver" in config file , I don't know if that's correct or not
[UPDATED]
Based on the controller code you showed, it didn't work because your code is being called using Builder style, and the package only works when it is called using Eloquent style.
Documentation link
So, maybe you need to change your code to:
$setting = Setting::where('id', 1)->firstOrFail();
$setting->update([
'expansion_id' => $request['expansion_id'],
'season' => $request['season'],
'advertiser_app' => $request['advertiser_app'],
'pvp_app' => $request['pvp_app'],
'raid_app' => $request['raid_app'],
'version' => $request['version']
]);
now I have another problem -_-
this is my controller:
$sql = Raid::findOrFail($request['id']);
$sql = $sql->update($request->all());
I have a array in my table , after update value will be like this:
"{\"Plate\":0,\"Cloth\":0,\"Mail\":0,\"Leather\":0}"
but it should be:
{"Plate":"0","Cloth":"0","Mail":"0","Leather":"0"}
so I will get an error
before this , I was updating like this and it was ok:
$sql = Raid::where('id', $request['id'])->update($request->all());
and this is my mode (traders and class_traders is fields that I have problem with):
use SoftDeletes;
use \OwenIt\Auditing\Auditable;
protected $table = 'raid';
protected $dates = ['date_and_time','deleted_at'];
protected $fillable = [
'admin_id', '....
];
protected $casts = [
'bosses' => 'array',
'traders' => 'array',
'class_traders' => 'array',
'boosters' => 'array',
];

Laravel - elastic search with Laravel Scout package softdelete is not working

I have implemented elasticsearch in laravel 5.5 using "babenkoivan/scout-elasticsearch-driver" package.
composer.json
"require": {
"babenkoivan/scout-elasticsearch-driver": "^2.2",
"elasticsearch/elasticsearch": "^5.1",
"laravel/scout": "^3.0"
}
GGIndexConfigurator file use to creat elastic search index.
<?php
namespace App;
use ScoutElastic\IndexConfigurator;
use ScoutElastic\Migratable;
class GGIndexConfigurator extends IndexConfigurator
{
use Migratable;
protected $settings = [
//
];
protected $defaultMapping = [
'properties' => [
'id' => [
'type' => 'integer',
],
'title' => [
'type' => 'string',
],
'img' => [
'type' => 'string',
],
'url' => [
'type' => 'string',
],
'type' => [
'type' => 'string',
],
'location' => [
'type' => 'geo_point',
],
'created_at' => [
'type' => 'date',
'format' => 'yyyy-MM-dd HH:mm:ss'
]
]
];
}
MySearchRule file tell elastic to search on title field.
<?php
namespace App;
use ScoutElastic\SearchRule;
class MySearchRule extends SearchRule
{
public function buildQueryPayload()
{
return [
'must' => [
'match' => [
'title' => $this->builder->query
]
]
];
}
}
MegaBrand file to store / update / remove record in elastic search in same pattern like add id , title , url , img etc..
<?php
namespace App;
use Carbon\Carbon;
use ScoutElastic\Searchable;
use Illuminate\Database\Eloquent\Model;
class MegaBrand extends Model
{
use Searchable;
protected $table = 'brands';
protected $primaryKey = 'brand_id';
protected $indexConfigurator = GlobalGarnerIndexConfigurator::class;
protected $searchRules = [
MySearchRule::class
];
protected $mapping = [
//
];
/**
* Get the index name for the model.
*
* #return string
*/
public function searchableAs()
{
return 'brands';
}
/**
* Get the indexable data array for the model.
*
* #return array
*/
public function toSearchableArray()
{
//$array = $this->toArray();
return [
'id' => $this->brand_id,
'title' => $this->brand_name,
'img' => $this->image,
'url' => $this->website,
'type' => 'brands',
'location' => [],
'created_at' => Carbon::now()->toDateTimeString()
];
}
}
above all code is for elastic search files and configurations. Now I run commend to create elastic search index.
php artisan elastic:create-index App\GGIndexConfigurator
index is create successfully with.
BrandModel file
<?php
namespace App\Model;
use App\GlobalGarnerIndexConfigurator;
use app\MySearchRule;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use ScoutElastic\Searchable;
class GiftIndiaModel extends Model
{
use SoftDeletes;
use Searchable;
protected $table = 'brands';
protected $primaryKey = 'brand_id';
protected $fillable = ['hash', 'brand_name', 'category', 'description', 'terms', 'image', 'discount', 'tat', 'isOnline', 'website', 'status', 'gg_brand_status'];
protected $indexConfigurator = GlobalGarnerIndexConfigurator::class;
protected $searchRules = [
MySearchRule::class
];
protected $mapping = [
//
];
/**
* Get the index name for the model.
*
* #return string
*/
public function searchableAs()
{
return 'brands';
}
/**
* Get the indexable data array for the model.
*
* #return array
*/
public function toSearchableArray()
{
//$array = $this->toArray();
return [
'id' => $this->brand_id,
'title' => $this->brand_name,
'img' => $this->image,
'url' => $this->website,
'type' => 'brand',
'location' => [],
'created_at' => Carbon::now()->toDateTimeString()
];
}
Upto this point all is good not have any issue. now I create new function to add brand in database using eloquent and that brand is also added in elastic search :)
public function addBrand(){
$brand = new GiftIndiaModel([
'hash' => 'qwertyy123',
'brand_name' => 'microsoft',
'category' => 'laptop',
'description' => 'its good brand',
'terms' => 'lorum ipsum',
'image' => 'www.dell.com/image',
'discount' => 5,
'tat' => 2,
'isOnline' => 'yes',
'website' => 'www.dell.com',
'status' => 1
]);
if($brand->save()){
return response()->json(true , 200);
}
return response()->json(false , 400);
}
So addBrand function is working fine and I have tried php artisan scout:import App\\Brand command to add brand in elastic search with all success.
Real issue is when I softdelete this brand it doesn't affect elastic search index and my softdelete brand is still available in elastic search.
Update Brand function
public function updateBrand(){
return GiftIndiaModel::where('brand_id', 3)->delete();
}
When I run this function brand is successfully soft deleted from table and deleted_at field store detele date time.But it's not reflecting to elastic index.
Any advice ?
As #Babenko tell me in mail that soft deleting is not supported right now.
I find other way to do this.
public function updateBrand()
{
$brand = GiftIndiaModel::where('brand_id', 3)->first();
$brand->deleted_at = date("Y-m-d");
$brand->save();
$brand->unsearchable();
}
In above code you can see I have used unsearchable() method to remove item from elastic search index after soft delete.
I have also create issue/feature request in github.
github
If anyone having issue while updating / inserting record in elastic search I recommend you to use save() method of model.

Table creaction on doctrine migration for custom behavior

I've created custom doctrine(1.2) behavior which should create tables for models (very simmilar to i18n behavior). I see this tables in schema.sql, and if i execute it everything is fine, but this is no such tables if my migrations diff (doctrine:generate-migrations-diff).
What i'm doing wrong?
class DescriptionableGenerator extends Doctrine_Record_Generator
{
protected $_options = array(
'className' => '%CLASS%Description',
'tableName' => '%TABLE%_description',
'fields' => array(),
'generateFiles' => false,
'table' => false,
'pluginTable' => false,
'children' => array(),
'options' => array(),
'cascadeDelete' => true,
'appLevelDelete' => false
);
public function __construct(array $options = array())
{
$this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options);
}
public function buildRelation()
{
$this->buildForeignRelation('Descriptions');
$this->buildLocalRelation();
}
public function setTableDefinition()
{
$this->hasColumn('lang', 'char', '2', array('notnull' => true));
$this->hasColumn('field', 'string', '255', array('notnull' => true));
$this->hasColumn('title', 'string', '255', array('notnull' => true));
$this->hasColumn('description', 'clob');
$this->hasColumn('compulsory', 'boolean', 1, array('notnull' => true, 'default' => 0));
$this->addListener(new DescriptionableListener());
}
}
Solved!
Problem appears due to command "php symfony doctrine:build-model".
So, if you have the same problem you should:
Remove your behavior from schema.
Execute "php symfony doctrine:build-model".
Add your behavior to schema.
Run "php symfony doctrine:generate-migrations-diff".
Chears! %)

Resources