YII + beforeSave() + getting max value from a table row - save

i'm struggling to get my beforesave in Yii Framework working this way:
when the user sends the form, beforesave() should fetch the highest number in a column called 'order' and insert a value of order+1 into current 'order' field.
After a few hours spent here reading posts i managed to compile this thing:
public function beforeSave()
{
if (parent::beforeSave())
{
if($this->isNewRecord)
{
$criteria->select='max(order) as myMaxOrder';
$get_it= new CActiveDataProvider(get_class($this),
array('criteria'=>$criteria,));
$got_it=$get_it->getData();
$whatweneed=$got_it[0]['myMaxOrder'];
$this->order=(int)$whatweneed+1;
}
return true;
}
else
return false;
}
The code gets the MAX from 'order' but i really did't know how to deal properly with the YII's getData() method, so i var_dumped it and saw that what i was looking was there but i still don't know how to access this value apart from doing
$whatweneed=$got_it[0]['myMaxOrder'];
Could you tell me how to do it right?

If you set up your database so that the Order id is the Primary Key (it should already be anyway), just set it to "Auto Increment". If Auto Increment is set on your Primary Key (id), then when you save() a Model in Yii without an id it will automatically save it with an id one higher than the max. You won't need to do anything in beforeSave() at all! It is free functionality.
But perhaps I am totally misunderstanding your question. Maybe this is not an auto-incrementing primary key column for some reason. In that case, something like this should work (assuming your model is Order and you column is also, for some reason, called "order"):
$maxOrderNumber = Yii::app()->db->createCommand()
->select('max(order) as max')
->from('order')
->queryScalar();
$this->order = $maxOrderNumber + 1;
Good luck!

Related

Dexie eachUniqueKey and Where Clause

I'm developing an application in Quasar/Electron and using Dexie/IndexedDB for my database. I want to find all distinct records in the database that contain both my Event ID and a Dog ID (both key indexed fields). I am able to do this with the following code:
await myDB.runTable
.orderBy('[fk_event+fk_dog]')
.eachUniqueKey((theDuo) => {
this.runsArray.push({eventID: theDuo[0], dogID: theDuo[1]})
})
I'm using a combined key which is working well. However, I need to have more of the records than just the keys. I need a few more fields, is this possible?
I was trying to get records with the unique key function while also using the where function, but that doesn't seem to work.
I need to get all the unique (distinct?) dogs in the table that are in a particular event. And also get their corresponding information. I'm not sure if there is a better, more efficient way to do this? I can always pull out all the records and loop through them to build a custom array, I was just hoping to do this at the table read level. (yeah I'm still in tables/records even though these are collections etc. :p ).
Even the above code gives me all the events, and I can pull out what I need with a filter. I just was thinking it would be faster and more efficient to do it at the read level.
this.enteredRuns = this.runsArray.filter((theEvent) => {
return ( (theEvent.eventID == this.currentEventID) )
})
Try
await myDB.runTable
.orderBy('[fk_event+fk_dog]')
.clone({unique: "unique"})
.toArray()
I know this isn't documented but it should do the work to use unique cursor while still extracting the whole objects and not just the keys. You cannot combine with where but you could use .filter. Just be aware that not all records with be scanned as it will jump over records with same keys - selecting the first visited records only.

Grails connect database fields

I'm trying to learn Grails but am still pretty much on beginner level.
I made a tiny application, where you can basically add events, and people can write reviews about them and rate them.
So I have an eventController and reviewController. The rating is just an Integer in a review. But now I want to show an overall rating for an event. So the events rating should be the average value of the corresponding ratings value.
This is my event domain code where the rating is initially set, I left out the constraints and toString:
class Event {
Double dRating = 2
static hasMany = [ratings:Rating]
}
The controller is simply:
class EventController {
def scaffold = Event
}
The rating domain file:
class Rating {
String comment
java.util.Date date = new java.util.Date()
Integer rating
Event event
}
and the Rating Controller is:
class RatingController {
def scaffold = Rating
}
Hope I didn't make mistakes, I had to translate variable names so they're understandable.
I'm guessing that where dRating is set, I could somehow add some calculation, but I don't even know how to access the values of the rating, and everything I try ends in lots of errors and having to restart the app again and again. I tried adding calculations in the controller, but there I don't know how to get the value into the database. So where do I actually put the logic, and how do I access the values?
If any other file is important, please tell me.
Hoping to get some hints on how to start doing this, I didn't think it would be this hard.
At first I would recommend start reading grails doc. Later from your question it is not much clear what you are asking a there could be several places or possibilities of setting up the value in domain field and persist it to database. I'm telling all of these which are known to me:
If it is generalised calculation that needs to be applied to your dRating field, then create a setter with standard bean naming convention and do this there. For example, you want to find percentage from say 1000 and then add it to dRating
class Event {
static hasMany = [ratings:Rating]
Double dRating
void setDRating(Double value){
this.dRating = value * 100/1000 // alternatively value /10
}
}
Do it in commandObject: If you don't want to put certain calculations and validation in domain then put these in command objects. See this. You can at any point of time assign values from command object to domain object by .properties binding mechanism. For example,
yourDomainObject.properties = yourcommandObjectObject.properties
Remember properties having same name would be binded.
Do it in service: You can do your calculations in service method, inject that service into your controller and call that method to perform calculations and even to persist to db as well. Remember services are by default transactional.
Hope it helps!

Using Umbraco Forms to edit data

I would like to use Umbraco Forms to not only insert data but to edit it as well. So far when I want to edit a record I am passing in the form guid and the record id via querystring and populating the correct data in the fields.
So far so good.
I am then hooking in to the Umbraco.Forms.Data.Storage.RecordStorage.RecordInserting event successfully like so
void RecordStorage_RecordInserting(object sender, Umbraco.Forms.Core.RecordEventArgs e)
{
var ms = (Umbraco.Forms.Data.Storage.RecordStorage)sender;
if(this record exists){
ms.UpdateRecord(e.Record, e.Form);
}
}
However when I try to submit an edited record, and the ms.RecordUpdate(e.Record, e.Form) line runs I get this error
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_UFRecordDataString_UFRecordFields_Key". The conflict occurred in database "UmbracoPlay", table "dbo.UFRecordFields", column 'Key'.
The statement has been terminated.
I can't delete the old record and then insert a new record because it will re raise the same event everytime I call ms.InsertRecord
What am I missing?
How can I use Umbraco Forms to edit existing data?
I couldn't see a fix for this bug- it appears as though the UpdateRecord method actually tries to insert all UFRecordField objects a second time rather than updating the existing values ( or the existing field values ) resulting in this key violation.
If you really need to work around this - as I did - then one thing that works ( but leaves you with somewhat more fragmented primary keys ) is simply to remove and then reinsert the form data:
var ms = (Umbraco.Forms.Data.Storage.RecordStorage)sender;
if(this record exists){
ms.DeleteRecord(e.Record, e.Form);
ms.InsertRecord(e.Record, e.Form);
}
An untidy solution, but seemingly effective.

Is it possible to save a specific id for a grails domain object?

I am in the process of trying to copy the properties of one domain object to another similar domain object (Basically moving retired data from an archive collection to an active one). However, when I try to save with a manually inputed id the save will not actually put anything into the collection.
def item = new Item(style: "631459")
item.id = new ObjectId("537da62d770359c2fb4668e2")
item.save(flush: true, validate: false, failOnError:true)
The failOnError does not throw an exception and it seems like the save works correctly. Also if I println on the item.save it will return the correct id. Am I wrong in thinking that you can put a specific id on a domain object?
You can set the id generator as 'assigned' so then you can put the value that you want and is going to be saved with that value.
class Item {
...
static mapping = {
id generator:'assigned'
}
}
The identifier id is a somewhat sensitive name to use. If you check your dbconsole, you will find that GORM has provided one for you even without asking. When you use that name for yourself, confusion happens. Grails will respect you with the println stuff, but GORM has the last word on how id gets initialized and stored, and it is not listening to you then.
You can rename the id to something else like you see in this post and maybe then you can use the name id for yourself. Otherwise, I suggest leaving id to GORM, and have your own identifier for your old keys. You won't have problems retrieving data anyway and there won't be performance issues.

Hydrating Database

I am new to learning and understanding how Hydration works, just wanted to point that out first. I'm currently able to Hydrate Select and Insert queries without any problems.
I am currently stuck on trying to Hydrate Update queries now. In my entity I have setup the get/set options for each type of column in my database. I've found that the ObjectProperty() Hydrator works best for my situation too.
However whenever I try to update only a set number of columns and extract via the hydrator I am getting errors because all the other options are not set and are returning null values. I do not need to update everything for a particular row, just a few columns.
For example in my DB Table I may have:
name
phone_number
email_address
But I only need to update the phone_number.
$entity_passport = $this->getEntityPassport();
$entity_passport->setPrimaryPhone('5551239876');
$this->getTablePassport()->update($this->getHydrator()->extract($entity_passport), array(
'employeeid' => '1'
));
This returns an error because setName() and setEmailAddress() are not included in this update and the query returns that the values cannot be null. But clearly when you look at the DB Table, there is data already there. The data that is there does not need to be changed either, only in this example does the PrimaryPhone() number.
I've been looking and reading documentation all over the place but I cannot find anything that would explain what I am doing wrong. I should note that I am only using Zend\Db (Not Doctrine).
I'm assuming I've missed something someplace due to my lack of knowledge with this new feature I'm trying to understand.
Perhaps you don't Hydrate Update queries... I'm sort of lost / confused. Any help would be appreciated. Thank you!
I think you're having a fundamental misconception of hydration. A hydrator simply populates an entity object from data (hydrate) and extracts data from an entity object (extract). So there are no separate hydrators for different types of queries.
In your update example you should first retrieve the complete entity object ($entity_passport) and then pass it to the TableGateway's update method. You would retrieve the entity by employeeid, since that's the condition you're using to update. So something like this:
$entity_passport = $passportMapper->findByEmployeeId(1);
$entity_passport->setPrimaryPhone('5551239876');
$this->getTablePassport()->update($this->getHydrator()->extract($entity_passport), array(
'employeeid' => $entity_passport->getId()
));
This is assuming you have some sort of mapper layer. Otherwise you could use your passport TableGateway (I assume that's what getTablePassport() returns, no?).
Otherwise, if you think retrieving the object is too much overhead and you just want to run the query you could use just a \Zend\Db\Sql\Sql object, ie:
$sql = new \Zend\Db\Sql\Sql($dbAdapter);
$update = $sql->update('passport')
->set(array('primary_phone' => $entity_passport->getPrimaryPhone()))
->where(array('employeeid' => $employeeId));
Edit:
Maybe it was a mistake to bring up the mapper, because it may cause more confusion. You could simply use your TableGateway to retrieve the entity object and then hydrate the returned row:
$rows = $this->getTablePassport()->select(array('employeeid' => 1));
$entity_passport = $this->getHydrator($rows->current());
[...]
Edit 2:
I checked your gist and I noticed a few things, so here we go:
I see that your getTablePassport indeed does return an object which is a subclass of TableGateway. You have already set up this class for it to use a HydratingResultset. This means you don't need to do any manual hydrating when retrieving objects using the gateway.
You also already implemented a Search method in that same class, so why not just use that? However I would change that method, because right now you're using LIKE for every single column. Not only is it very inefficient, but it will also give you wrong results, for example on the id column.
If you were to fix that method then you can simply call it in the Service object:
$this->getTablePassport->Search(array('employeeid' => 1));
Otherwise you could just implement a separate method in that tablegateway class, such as
public function findByEmployeeId($employeeId)
{
return $tableGateway->select(array('employeeid' => $employeeId));
}
This should already return an array of entities (or one in this specific case). P.S. make sure to debug and check what is actually being returned when you retrieve the entity. So print_r the entity you get back from the PassportTable before trying the update. You first have to make sure the retrieval code works well.

Resources