Is there a way to get a table's primary key name using the Laravel query builder? Can i do something like
$key = DB::table($someTable)->getPrimaryKey();
I've looked through the documentation and API but couldn't find any reference to it.
Thanks in advance.
You can get an array of indexes for a table directly using the Schema manager:
$indexes = DB::connection()->getDoctrineSchemaManager()->listTableIndexes($table);
OR
$indexes = Schema::getConnection()->getDoctrineSchemaManager()->listTableIndexes($table);
And then you can get an array of the columns associated with that index key:
$columns = $indexes[ 'primary' ]->getColumns();
This function can be used to find the primary key(s) or unique key(s) for a table you know nothing about:
public function getTablePrimaryOrUniqueKey($table, $key='') {
//get the array of table indexes
$indexes = DB::connection()->getDoctrineSchemaManager()->listTableIndexes($table);
//abort if there are no indexes
if(!is_array($indexes)) return false;
//set up the default array of index keys
$keys = ['primary', 'unique'];
//if a key was passed and it is valid, use it...but cast as an array
if($key!='' && in_array($key, $keys)) $keys = (array) $key;
//loop through the keys array
foreach ( $keys as $key ) {
//keep it honest
if ( array_key_exists( $key, $indexes ) ) {
//get the columns for this key
$columns = $indexes[ $key ]->getColumns();
//if we have an array of columns, we have what we need
if ( is_array( $columns ) ) {
return $columns;
}
}
}
//if you got here, you did not get find what you were looking for
return false;
}
The Illuminate/Database/Eloquent/Model class has a getKeyName function which is public.
Inside your model class, you can access the primary key using $this->getKeyName().
my approach is :
$result = DB::select(DB::raw("SHOW KEYS FROM {$table} WHERE Key_name = 'PRIMARY'"));
$primaryKey = $result[0]->Column_name;
and i use mysql
You can use ->getKey() on a model:
$someModel->getKey();
Or if you're just trying to get the name of the column, without having an instance of the model:
app(App\Model::class)->getKeyName()
$primary_key = app($builder->getModel())->getKeyName();
if you referenced the model when you initialized that Query Builder instance you can retrieve the model reference then use it to get the keyName. but if your just directly called a new Query Builder there isn't a way to see the primary key without doing a select against the table keys, maybe in a subselect?
SHOW KEYS FROM table WHERE Key_name = 'PRIMARY'
Related
Hi I am trying to get customer _id from different tables Purchase order ,Sale Order and Consignments
Then I am looping through these Ids . Method I am using for this purpose is working perfectly but . I am afraid if there is a lot of data this method may get failed. Here is my method .
$consignmentCustomerIds = Consignment::select('customer_id')->where('is_repeat', 0)->whereDate('created_at','>',date('2021-03-06'))->whereRaw('(is_group = "parent" or is_group is null)')->where('finalize', 0)->where('invoice_id', null)->distinct()->pluck('customer_id')->toArray();
$poCustomerIds = PurchaseOrder::select('customer_id')->whereDate('created_at','>',date('2021-03-06'))->where('invoice_id', null)->distinct()->pluck('customer_id')->toArray();
$soCustomerIds = SaleOrder::select('customer_id')->whereDate('created_at','>',date('2021-03-06'))->where('invoice_id', null)->distinct()->pluck('customer_id')->toArray();
$spCustomerIds = StoragePeriod::select('customer_id')->whereDate('created_at','>',date('2021-03-06'))->where('invoice_id', null)->distinct()->pluck('customer_id')->toArray();
$ids = array_merge($consignmentCustomerIds, $poCustomerIds, $soCustomerIds, $spCustomerIds);
$customers = Customer::whereIn('id', $ids)->get();
foreach ($customers as $customer) {
CreateInvoiceOneByOne::dispatch($customer)->onQueue('invoice');
}
Is there any better way of doing so?
The main thing is to change ->get() to ->cursor() in the iteration:
// $customers = Customer::whereIn('id', $ids)->get();
$customers = Customer::whereIn('id', $ids)->cursor();
The cursor method may be used to significantly reduce your application's memory consumption when iterating through tens of thousands of Eloquent model records.
More info: https://laravel.com/docs/8.x/eloquent#cursors
IF YOUR RELATIONS ARE SET PROPERLY
I suggest to reduce database query. You can do this by chaning whereHas and orWhereHas within the customer request.
Querying Relationship Existence
$date = date('2021-03-06');
$customers = Customer::whereHas('consignment', function($query) use($date) {
$query->where('is_repeat', 0)->whereDate('created_at','>',$date)->whereRaw('(is_group = "parent" or is_group is null)')->where('finalize', 0)->where('invoice_id', null);
})->orWhereHas('purchase_order', function($query) use($date) {
$query->whereDate('created_at','>',$date)->where('invoice_id', null);
})->orWhereHas('sale_order', function($query) use($date) {
$query->whereDate('created_at','>',$date)->where('invoice_id', null);
})->orWhereHas('storage_period', function($query) use($date) {
$query->whereDate('created_at','>',$date)->where('invoice_id', null);
})->get();
foreach ($customers as $customer) {
CreateInvoiceOneByOne::dispatch($customer)->onQueue('invoice');
}
I set the $date variable before the query, so this way you can manipulate it at one place.
P.S. I am currently assuming the name of the relations.
I wanted to know what the best way is to store single data values in a database. Values you only need one field of.
I have a couple of ideas.
Storing all fields in a single_data_table with a id, key and value column.
Storing it as a json object in the database.
Storing it as an array in the database.
Which one is the best way? Or might there even be better ways?
Or is it just easier to keep single data values as static data on the webpage?
At my current job we use a separate settings table for that, with two columns: key and value. And then there's a simple function to retrieve or save a setting:
function setting($key, $value = null){
$setting = DB::table('settings')->where('key', $key)->first();
if(is_null($value)){
return $setting->value ?? null;
}
if(isset($setting)){
$setting->value = $value;
}else{
$setting = $setting ?: new stdClass();
$setting->key = $key;
$setting->value = $value;
}
DB::table('settings')->insertOrReplace((array) $setting);
return true;
}
It is used like so:
$phone = setting('phone'); // Get the phone setting
$url = setting('url', 'http://example.com'); // Set url setting
I'm going to check if a row exists in my DB which one of its field matches a custom value.
e.g. consider table licences which contains fields: (id,serial,validity).
I'm going to check two conditions in my controller:
licence with serial 'xyz' is presents in db
licence with serial 'xyz' have validity field value 'valid'
How should i complete $option for this code:
public function validity($serial = null) {
$this->autoRender = false; // We don't render a view in this example
$options = ?????;
$license = $this->License->find('first', $options);
if ($license){
// it is valid and present
$data = array('validity' => 'valid');
);
}else{
//not present actions
$data = array('validity' => 'invalid');
}
$this->response->body(json_encode($data));
}
The options argument has a lot of parameters you can use like conditions, order and fields. In your case you need conditions
$options=array('conditions'=>array('License.serial'=>'xyz', 'License.validity'='valid'));
(by default it is AND between conditions)
I have the following one to many relationship setup between two tables (Case and Recipient)
Case table has one primary key which is identified as an identity (auto-increment) tblRecipient has one primary key which is identified as an identity (auto-increment),and a foreign key relationship with Case.
Case table and related tblRecipient data is pulled:
function searchCases(caseID, caseNum)
{
var qPredicate;
if (caseID != '')
{
qPredicate = new breeze.Predicate("pkCaseID", "==", parseInt(caseID));
}
else if (caseNum != null)
{
qPredicate = new breeze.Predicate("CaseNumber", "Contains", caseNum);
}
var query = breeze.EntityQuery
.from("case")
.where(qPredicate)
.expand("tblRecipients")
return manager.executeQuery(query);
}
When a button is pressed to add a new recipient, the following code is used to create a new recipient entity:
function createNewRecipient(CaseID)
{
var recipientEntityType = manager.metadataStore.getEntityType("tblRecipient");
var newRecipient = manager.createEntity(recipientEntityType, {fkCaseID: CaseID});
return newRecipient;
}
This code returns this error:
Error: Cannot attach an object to an EntityManager without first setting its key or setting its entityType 'AutoGeneratedKeyType' property to something other than 'None'
The AutoGeneratedKeyType in the metadata shows None instead of Identity as in the Case table. We are not sure what we need to change or how to really debug this. Any help would be appreciated.
I have a CakePHP model - User - that has ties to an external corporate system. I store some data on those systems and other data locally. In my User::beforeSave() method, I'm trying to set an ID, send the data (with that custom ID) to my corporate systems and then, if it inserts successfully there, return true so that Cake will insert the new user record with that same ID so that I can link them later.
I can't find a way to make this happen. Is there a way to insert a CakePHP record with a user-specified primary key value? I'm using UUIDs so there's (effectively) no opportunity for overlap.
$this->data['User']['id'] = String::uuid()
try {
$user_proxy = new CoreServicesUserProxy();
$corp_user = $user_proxy->CreateUser (
array (
'user' => array (
'UserName' => 'myusername',
'EmailAddress' => $this->data['User']['email'],
'SecurityId' => $this->data['User']['id']
)
)
);
}
catch ( Exception $e ) {
// error handling stuff
return false;
}
I realise you have already been given some hints, but here is some code which might help.
Why not add an external_user_id field to your users table?
<?php
class User extends AppModel {
function beforeSave() {
$ds = ConnectionManager::getDataSource('core_services');
$externalUser = $ds->createUser($this->data);
if (!$externalUser) {
return false;
}
$this->data['User']['external_id'] = $externalUser['id'];
return true;
}
function afterFind($results, $primary) {
// handle different types of find here ('all' vs 'first' vs through relation)
foreach ($results as &$result) {
$result = $this->_mergeExternalUser($result);
}
}
function _mergeExternalUser($user) {
$ds = ConnectionManager::getDataSource('core_services');
$externalUser = $ds->retrieveUser($result['external_id']);
return am($externalUser, $user);
}
}
?>
There is a way - but typically you would add another column to the Users table instead and let CakePHP do it's thing with the primary key. See this Bakery article to know how it's done. Since it is more than a year later, this is for reference mostly. As far as I understand it, this should function well with CakePHP 1.2 as well.