I am creating a real estate website that has Listing ID's attached to each listing. I am running a script via shell in CakePHP that is parsing a csv file and should be updating any listing that already exists or inserting a new one if it does not.
The problem is that I keep getting a Duplicate entry '###' for key "PRIMARY' where ### is the Listing ID that is being provided by the CSV. This script is being run from the command line.
Here's a smaller version of what my table includes:
CREATE TABLE `listings` (
`ListingID` int(11) NOT NULL,
`AccessibilityYN` varchar(50) DEFAULT NULL COMMENT 'AccessibilityYN',
`BathsFull` int(6) DEFAULT NULL COMMENT 'BathsFull',
`BathsPartial` int(6) DEFAULT NULL COMMENT 'BathsPartial',
`BathsTotal` decimal(5,1) DEFAULT NULL COMMENT 'BathsTotal',
`Beds` int(11) DEFAULT NULL COMMENT 'Beds',
PRIMARY KEY (`ListingID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Here's my Listing Model (notice that I have public $primaryKey = 'ListingID';)
class Listing extends AppModel {
public $name = 'Listing';
public $primaryKey = 'ListingID';
public $belongsTo = array(
'Agent' => array(
'className' => 'Agent',
'foreignKey' => 'AgentID'
)
);
}
Here's the shell I'm running via command line:
class MyShell extends AppShell {
public $uses = array('Listing');
public function update_imported_listings() {
/***SOME CODE HERE TO GET THE $entry FROM THE CSV***/
$this->Listing->ListingID = $entry['ListingID'];
if(!$this->Listing->exists()){
$this->Listing->create();
}
if($this->Listing->save($entry)){
echo "Saved Listing";
} else {
echo "Listing Failed";
}
}
}
I understand that CakePHP usually likes id to be the field used in the database, however I have set $primaryKey = 'ListingID' in my model. I've tried setting ListingID to Auto Increment in the DB but that didn't work.
Anybody have any ideas? I'm fresh out.
Setting ListingID doesnt' do anything
This line is your problem:
$this->Listing->ListingID = $entry['ListingID'];.
Irrespective of what your actual primary key field is in database the primary key value is always specified using Model->id property. As such, change it to:
$this->Listing->id = $entry['ListingID'];
You don't need to call exists
There is not need to explicitly check if record with particular primary key values exists. Cake will automatically update and record instead of creating a new one if the data array passed to save() contains a valid primary key value which exists in db. Just ensure you call create() before saving if you are using save() in a loop.
Don't break CakePHP conventions, use 'id' field as primary key.
http://book.cakephp.org/view/24/Model-and-Database-Conventions
I was getting the same error running a cakePHP shell script, turns out the latest record's ID was at the maximum value allowed by the size of the column (whoever made the table used a medium int for the id column), so auto_increment wasn't working. Changed the column to allow for larger numbers to fix. Probably something obvious, but might help someone out there.
Related
I am learning Laravel 8 and came across this problem. So, I made a controller class with FacadesDB Insert function. This is my code :
public function aksimenulis_laporan(Request $request){
$filefoto = $request->foto;
DB::table('pengaduan')->insert([
'tgl_pengaduan' => date('Y-m-d'),
'nik' => $_POST['nik'],
'isi_laporan' => $_POST['isi_laporan'],
'foto' => $filefoto->getClientOriginalName(),
'status' => '0',
]);
// isi dengan nama folder tempat kemana file diupload
$tujuan_upload = storage_path();
// upload file
$filefoto->move($tujuan_upload,$filefoto->getClientOriginalName());
return redirect('');
}
https://i.stack.imgur.com/VTJyS.png
This is my database table structure.
And I am trying to insert 'foto' with value of 'id_pengaduan' or the primary key, like using this code. Which will be PrimaryKey_OriginalFileName
'foto' => $filefoto-> 'id_pengaduan'.'_'.getClientOriginalName(),
But the problem I didn't have the value yet. Is there a way that I can value 'foto' with the primary key of the table?
you can use the insertGetId method to insert a record and then retrieve the ID:
$pengaduan_id= DB::table('pengaduan')->insertGetId([
'tgl_pengaduan' => date('Y-m-d'),
'nik' => $_POST['nik'],
'isi_laporan' => $_POST['isi_laporan'],
'status' => '0',
]);
DB::table('pengaduan')->where('id',$pengaduan_id)->update(['foto'=>$filefoto->getClientOriginalName()]);
you can not get the id before the insertion operation is done.
you also should do it in transaction, but for now, I will keep it this way.
You cannot (and shouldn't) insert a row and define its primary key. That's kind of the whole purpose of a primary key. You let SQL manage its identifiers, this is good incase of an error, so there are no duplicates in the primary key.
I'll go into eloquent since you're starting new in Laravel, this will make your life easier with DB operations.
First you'd need to make a new model for your table. You can do this using the console command:
php artisan make:model ModelName
Afterwards, navigate to your Models directory (app/Models/) and you'll find the new model ModelName.php
In there, you'd need to specify the table name and the primary key. You can do that with the these two lines:
protected $table = 'pengaduan';
protected $guarded = ['id_pengaduan'];
Your model should now look something like
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ModelName extends Model
{
use HasFactory;
protected $table = 'pengaduan';
protected $guarded = ['id_pengaduan'];
}
Save, and you might need to clear your cache. Do this by running php artisan optimize in the console.
After you are done inserting the data into a new row, you can get the primary key that was assigned to the row like this:
//using eloquent
use App\Models\ModelName;
//insert data
$insertRow = new Pengaduan;
$insertRow->tgl_pengaduan = date('Y-m-d');
$insertRow->nik = $_POST['nik'];
$insertRow->isi_laporan= $_POST['isi_laporan'];
$insertRow->foto = $filefoto->getClientOriginalName();
$insertRow->status = 0;
$insertRow->save();
//get key here
$rowId = $insertRow->pengaduan_id;
If you want to use the DB::table('table')->insert method, see OMR's answer.
If you really want to define the primary key yourself, then you'd have to run this query on the table:
SET IDENTITY_INSERT [Tablename] ON;
You can read more about this on this thread.
I have a dropdown in my Yii form created with CActiveForm->dropDownList.
I am using the array('empty'=>'Please select one') as the way to set the value when nothing is selected.
The field is constrained to another table, but it is set to allow NULL. When Yii shows the MySQL error, it says:
integrity constraint violation: 1452 Cannot add or update a child row: a foreign
key constraint fails (`imi_sales`.`ss_project`, CONSTRAINT
`fk_project_product_type` FOREIGN KEY (`product_type_id`) REFERENCES
`ss_product_type` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION)
It also shows that the value being inserted into ss_project.product_type_id is ''. If I copy the SQL out and change it to NULL, it works. How can I have Yii set it to NULL?
Add the following codes into beforeSave() method:
public function beforeSave(){
if(parent::beforeSave()){
if(empty($this->product_type_id)) {$this->product_type_id=NULL;}
return true;
}
}
By this, you tell Yii before saving new record, check whether product_type_id value is empty('') or not. If yes, set it NULL.
The selected answer works but I believe this is more elegant.
/**
* #return array validation rules for model attributes.
*/
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
....................
array('product_type_id', 'default', 'setOnEmpty' => true, 'value' => null),
....................
);
}
<?php
// Model
class ProfileDelivery extends \Eloquent {
protected $table = 'profile_delivery';
protected $guarded = array();
public $timestamps = FALSE;
}
// Somewhere
$deliveryGuy->id = 1;
print $deliveryGuy->id; // Prints 1
if (!$deliveryGuy->save()) {
throw new \Exception('Cant save .');
}
print $deliveryGuy->id; // Prints 0
Can anyone explain me why the ID value is lost?
Not sure if you solved this for your situation but in Laravel 5.1 this just happened to me - the primary key of one table is the same as the primary key to another table because there is a 0 or 1 - to - 1 relationship between them.
What is happening is that Eloquent is assigning the primary key to the last insert id of the insert, but because the primary key is not an auto increment value, it is assigning it to zero. It is stored in the database correctly, but the model after the save is not useful if you need to use that key. The solution is to override the insertAndSetId function for the model that has the foreign primary key to prevent its setting of the primary key attribute. Of course, you don't want to do this for any models that do have an auto-incrementing key, just models that you are assigning the primary key manually. It's also not necessary if you don't need to use the model immediately after creating it because as I said above the database has the correct info in it.
protected function insertAndSetId(Builder $query, $attributes)
{
$id = $query->insertGetId($attributes, $keyName = $this->getKeyName());
// $this->setAttribute($keyName, $id);
}
This is because your id column in the database probably does not have autoincrement set.
I tried this with a test model without autoincrement and it returns 0, but when I changed the id column to autoincrement it returned the id correctly.
Check this function in laravel/Framework/Src/Illuminate/Database/Eloquent/Model.php
It says it will insert and set id if it has autoincrement.
protected function performInsert($query)
{
if ($this->fireModelEvent('creating') === false) return false;
// First we'll need to create a fresh query instance and touch the creation and
// update timestamps on this model, which are maintained by us for developer
// convenience. After, we will just continue saving these model instances.
if ($this->timestamps)
{
$this->updateTimestamps();
}
// If the model has an incrementing key, we can use the "insertGetId" method on
// the query builder, which will give us back the final inserted ID for this
// table from the database. Not all tables have to be incrementing though.
$attributes = $this->attributes;
if ($this->incrementing)
{
$this->insertAndSetId($query, $attributes);
}
// If the table is not incrementing we'll simply insert this attributes as they
// are, as this attributes arrays must contain an "id" column already placed
// there by the developer as the manually determined key for these models.
else
{
$query->insert($attributes);
}
// We will go ahead and set the exists property to true, so that it is set when
// the created event is fired, just in case the developer tries to update it
// during the event. This will allow them to do so and run an update here.
$this->exists = true;
$this->fireModelEvent('created', false);
return true;
}
For me, I had to set protect $primaryKey to the column name of the primary key in the model to solve the issue. (skill_id was the column name so in the Skill model I set protected $primaryKey = 'skill_id', default is 'id'.)
This is my Controller
userdetails.php
<?php defined('SYSPATH') or die('No direct script access.');
class Controller_Userdetails extends Controller {
public function action_index() {
$view = new View('userdetails/index');
$this->response->body($view);
}
public function action_add() {
$userdetails = new Model_Userdetails();
$view = new View('userdetails/adddetails');
$view->set("userdetails", $userdetails);
$this->response->body($view);
}
model is
userdetails.php
<?php defined('SYSPATH') or die('No direct script access.');
class Model_Userdetails extends ORM {
}
userinfo.sql
CREATE TABLE `kohana`.`userinfo` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`first_name` varchar(100) DEFAULT NULL,
`last_name` varchar(100) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`description` text,
PRIMARY KEY (`id`)
);
I am newly learning php and kohana.I am using kohana 3.2 version.I am following this tutorial to create,edit,update and delete data in database.I tried with above code,i am getting this error
"Database_Exception [ 1146 ]: Table 'kohana.userdetailses' doesn't exist [ SHOW FULL COLUMNS FROM `userdetailses` ]"
need some help to solve this.
Kohana guesses the table name if $_table_name is not set. It pluralizes the model name as most of the time a model describes a single object, and the table describes a lot of them.
EDIT:
I saw you actually named your table userinfo and the model userdetails. If that is what you want:
class Model_Userdetails {
protected $_table_name = 'userinfo';
........
}
Or alternatively rename the model to Model_Userinfo, or the table to userdetails and:
--- END EDIT ---
In your case it would seem most appropiate to:
class Model_Userdetails {
protected $_table_name = 'userdetails';
........
}
Offcourse, you could alternatively rename your class to Model_Userdetail so that the table name will be guessed as userdetails
UPDATE:
Even though Kohana should guess these as well (i.e. SHOW FULL COLUMNS FOR table or sth), this might resolve the property not found error as discussed below in the comments.
protected $_table_columns = array(
'id' => array('type' => 'int'),
'firstname' => array('type' => 'string'),
....... // repeat for all columns
);
Update:
There is a typo in your PHP code: deScription. Anyway, it is a good habit to define the table columns as this will save you an extra query every time a model is initialized for the first time. Along with some validation stuff which can take parameters from the array (e.g. the description of type TEXT is more or less not restricted in length, but you might want to limit it to say 2000 characters)
I have a Competition model, that has many Entry models.
[edit] Schema looks (roughly) like this:
Competition:
id INT(11)
name VARCHAR(50)
date DATETIME
Entry:
id INT(11)
competition_id INT(11)
user_id INT(11)
answer VARCHAR(50)
isWinner INT(1)
In my pickWinner view, I have a form that loops through all related entries - offering the isWinner field to allow the user to pick an entry as the winner. Saving the related model etc is pretty standard and that all works fine.
I'm trying to validate the form so that at least one of the Entry models has isWinner set to true (the user has to pick at least one winner).
I obviously can't apply the validation rule to the Entry model - as each model only knows about itself and not the values of the other models.
Only one Entry model should be set as the winner - how do I add a validation rule to Competition, so that it can detect that one of its child Entry models has isWinner set to true?
One way to achieve this would be to add a relation to the Competition model to detect if it has a winner, something like;
public function relations()
{
return array(
...
'winners' => array(self::STAT, 'Entry', 'competition_id', 'condition'=>'`t`.`isWinner` = true'),
...
);
}
Then the following should return the number of winners for the given competition:
$competition = Competition::model()->findByPk($id);
$winners = $competition->winners;
Not tested, so you may need to alter a little.
EDIT
Ok, to get this info before you save you could do something like the following: if for example in your pickWinner view you have a field for each model and it's submitting back as an array, for example like $_POST[Entry][$model->id]['isWinner'] for each model, can you not simply cycle through those making sure one is set to true? e.g:
$winners = 0;
foreach(array_keys($_POST[Entry]) as $key)
{
if($_POST[Entry][$key]['isWinner']=='true')
$winners++;
}
if($winners==0)
{
echo "You selected no winners.";
} else if($winners>1) {
echo "You selected too many winners.";
} else if($winners==1) {
echo "Woot, 1 winner!";
}
Again, this depends on how your pickWinner form is laid out.