Use Laravel factories to set 'default' values for new models? - laravel

I have always thought of Laravel factories as a 'development' aid for generating fake data for testing purposes.
Recently, I inherited a Laravel 8 project where this is used in part for that, but it is also called from the production code to essentially insert 'default data' for a new entity that is being created.
(Think: We're creating a Company::class, and so we're going to also create blank contact profiles (CompanyContact::class) for its 'president', 'vice-president', and 'treasurer' with some basic placeholder text.)
That obviously works but when trying to determine where this default data was even coming from, it never occurred to me to look in database seeders/factories.
I would rather have expected to see a "created" event responder, either setup in Model::boot() or as a standalone event.
Aside from confusing future developers (or just me?), are there any other gotchas to using (misusing?) factory methods as a means of setting up 'default data'?

I got used to seeding the website settings or even some default data like Countries by using Seeders.
Of course, triggering events when seeding these data will be a good solution, but also I think the simple solution is leaving the created_at column null to determine the source of the data.

Related

Model changed during database created

I have uploaded my MVC3 project , it's s simple blog , at first it works well but after couple hours! following error appears (I've made custom error to Off to see the error)
The model backing the 'SiteContext' context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance. For example, the DropCreateDatabaseIfModelChanges strategy will automatically delete and recreate the database, and optionally seed it with new data.
to solve this I have to manually delete my database and create again and then restore to the backup that I have created. but after after 2 hours again I get the error!
I really don't have any idea , what caused that ??
When you create a model and ask EF to create a database from it, EF would hash the model and store the hash value with the database. Whenever the context is created, EF recomputes the hash and matches it against what is stored at the database. If the model changes in any way, the resulting hash will be different and EF will throw the exception you have just seen. This is done in order to keep the model in sync with the database.
Is there any way the model could have changed during runtime?
One thing you could do to figure out the difference is to
1.Re-create the database from the model as you are doing now and get it scripted (script1.sql).
2.Wait till the error happens and delete the db and re-create it again and script it (script2.sql)
3.Try to compare the two and see whether you can spot a difference in the schemas.
This should give you an idea of what has changed in the model.
Goodluck

how can i update an object/entity that is not completely filled out?

I have an entity with several fields, but on one view i want to only edit one of the fields. for example... I have a user entity, user has, id, name, address, username, pwd, and so on. on one of the views i want to be able to change the pwd(and only the pwd). so the view only knows of the id and sends the pwd. I want to update my entity without loading the rest of the fields(there are many many more) and changing the one pwd field and then saving them ALL back to the database. has anyone tried this. or know where i can look. all help is greatly appreciated.
Thx in advance.
PS
i should have given more detail. im using hibernate, roo is creating my entities. I agree that each view should have its own entity, problem is, im only building controllers, everything was done before. we were finders from the service layer, but we wanted to use some other finders, they seemed to not be accessible through the service layer, the decision was made to blow away the service layer and just interact with the entities directly (through the finders), the UserService.update(user) is no longer an option. i have recently found a User.persist() and a User.merge(), does the merge update all the fields on the object or only the ones that are not null, or if i want one to now be null how would it know the difference?
Which technologies except Spring are you using?
First of all have separate DTOs for every view, stripped only to what's needed. One DTO for id+password, another for address data, etc. Remember that DTOs can inherit from each other, so you can avoid duplication. And never pass business/ORM entities directly to view. It is too risky, leaks in some frameworks might allow users to modify fields which you haven't intended.
After the DTO comes back from the view (most web frameworks work like this) simply load the whole entity and fill only the fields that are present in the DTO.
But it seems like it's the persistence that is troubling you. Assuming you are using Hibernate, you can take advantage of dynamic-update setting:
dynamic-update (optional - defaults to false): specifies that UPDATE SQL should be generated at runtime and can contain only those columns whose values have changed.
In this case you are still loading the whole entity into memory, but Hibernate will generate as small UPDATE as possible, including only modified (dirty) fields.
Another approach is to have separate entities for each use-case/view. So you'll have an entity with only id and password, entity with only address data, etc. All of them are mapped to the same table, but to different subset of columns. This easily becomes a mess and should be treated as a last resort.
See the hibernate reference here
For persist()
persist() makes a transient instance persistent. However, it does not guarantee that the
identifier value will be assigned to the persistent instance immediately, the assignment
might happen at flush time. persist() also guarantees that it will not execute an INSERT
statement if it is called outside of transaction boundaries. This is useful in long-running
conversations with an extended Session/persistence context.
For merge
if there is a persistent instance with the same identifier currently associated with the session, copy the state of the given object onto the persistent instance
if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance
the persistent instance is returned
the given instance does not become associated with the session, it remains detached
persist() and merge() has nothing to do with the fact that the columns are modified or not .Use dynamic-update as #Tomasz Nurkiewicz has suggested for saving only the modified columns .Use dynamic-insert for inserting not null columns .
Some JPA providers such as EclipseLink support fetch groups. So you can load a partial instance and update it.
See,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/AttributeGroup

Using Oracle's GUID()-generated ID's in Grails/Hibernate

I trying to use Grails Scaffolding to throw a quick CRUD application together around some legacy database tables. It is an Oracle database, and the primary key value is intended to be populated by Oracle's GUID() function.
Based on this earlier StackOverflow question, I tried specifying "guid" as the Hibernate generator for this column in my Grails domain class:
...
static mapping = {
table name: "OWNER"
version false
columns {
id column: "OWNER_OID", generator: "guid"
name column: "NAME"
...
}
}
...
When I run my Grails app, viewing and even editing records works just fine. However, when I try to create a new record, things blow up with the Oracle error message "ORA-02289: sequence does not exist".
I enabled SQL logging for my datasource, and see Grails/Hibernate trying to execute the following during a save operation:
select hibernate_sequence.nextval from dual
This doesn't look right at all, and doesn't match the generated SQL from that earlier StackOverflow question linked above. Does anyone see something I am missing here, or otherwise know how to make Grails/Hibernate populate a primary key column with Oracle GUID values?
Whew... after another day of wrestling with this, I think I have my arms around the thing. This answer covers a bit more ground than the original question description, but that's because I found yet more problems after getting past the Hibernate generator issue.
Issue #1: Getting an Oracle GUID() value
As covered by Adam Hawkes' answer, the "guid" Hibernate generator is unmaintained and only works for older versions of the Oracle dialect.
However, if you use the Hibernate generator "assigned" (meaning that you want to set primary keys manually rather than have Hibernate auto-generate them), then you can insert values pulled from an Oracle SYS_GUID() call.
Even though Hibernate's newer Oracle dialects don't support "guid" seamlessly, they still understand the SQL necessary to generate these values. If you are inside of a Controller, you can fetch that SQL query with the following:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
If you are inside of a domain class instead, you can still do this... but you will need to first inject a reference to grailsApplication. You probably want to do this in a Controller, though... more on this below.
If you're curious, the actual String returned here (for Oracle) is:
select rawtohex(sys_guid()) from dual
You can execute this SQL and fetch the generated ID value like this:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Issue #2: Actually using this value in a Grails domain object
To actually use this GUID value in your Grails domain class, you need to use the Hibernate generator "assigned". As mentioned earlier, this declares that you want to set your own ID's manually, rather than letting Grails/GORM/Hibernate generate them automatically. Compare this modified code snippet to the one in my original question above:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
In my domain class, I changed "guid" to "assigned". I also found that I needed to eliminate the "columns {}" grouping block, and move all my column information up a level (weird).
Now, in whichever Controller is creating these domain objects... generate a GUID as described above, and plug it into the object's "id" field. In a Controller generated automatically by Grails Scaffolding, the function will be "save()":
def save() {
def ownerInstance = new Owner(params)
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
ownerInstance.id = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
if (!ownerInstance.save(flush: true, insert: true)) {
render(view: "create", model: [ownerInstance: ownerInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'owner.label', default: 'Owner'), ownerInstance.id])
redirect(action: "show", id: ownerInstance.id)
}
You might think to try putting this logic directly inside the domain object, in a "beforeInsert()" function. That would definitely be cleaner and more elegant, but there are some known bugs with Grails that prevent ID's from being set in "beforeInsert()" properly. Sadly, you'll have to keep this logic at the Controller level.
Issue #3: Make Grails/GORM/Hibernate store this properly
The plain truth is that Grails is primarily intended for virgin-new applications, and its support for legacy databases is pretty spotty (in fairness, though, it's a bit less spotty than other "dynamic" frameworks I've tried). Even if you use the "assigned" generator, Grails sometimes gets confused when it goes to persist the domain object.
One such problem is that a ".save()" call sometimes tries to do an UPDATE when it should be doing an INSERT. Notice that in the Controller snippet above, I have added "insert: true" as a parameter to the ".save()" call. This tells Grails/GORM/Hibernate explicitly to attempt an INSERT operation rather than an UPDATE one.
All of the stars and planets must be in alignment for this to work right. If your domain class "static mapping {}" block does not set the Hibernate generator to "assigned", and also set "version false", then Grails/GORM/Hibernate will still get confused and try to issue an UPDATE rather than an INSERT.
If you are using auto-generated Grails Scaffolding controllers, then it is safe to use "insert: true" in the Controller's "save()" function, because that function in only called when saving a new object for the first time. When a user edits an existing object, the Controller's "update()" function is used instead. However, if you are doing your own thing in your own custom code somewhere... it will be important to check on whether a domain object is already in the the database before you make a ".save()" call, and only pass the "insert: true" parameter if it really is a first-time insert.
Issue #4: Using natural keys with Grails/GORM/Hibernate
One final note, not having to do with Oracle GUID values, but related to these Grails issues in general. Let's say that in a legacy database (such as the one I've been dealing with), some of your tables use a natural key as their primary key. Say you have an OWNER_TYPE table, containing all the possible "types" of OWNER, and the NAME column is both the human-readable identifier as well as the primary key.
You'll have to do a couple of other things to make this work with Grails Scaffolding. For one thing, the auto-generated Views do not show the ID field on the screen when users are creating new objects. You will have to insert some HTML to the relevant View to add a field for the ID. If you give the field a name of "id", then the auto-generated Controller's "save()" function will receive this value as "params.id".
Secondly, you have to make sure that the auto-generated Controller's "save()" function properly inserts the ID value. When first generated, a "save()" starts off by instantiating a domain object from the CGI parameters passed by the View:
def ownerTypeInstance = new OwnerType.get( params )
However, this does not handle the ID field you added to your View. You will still need to set that manually. If on the View you gave the HTML field a name of "id", then it will be available in "save()" as "params.id":
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
Piece of cake, huh? Perhaps "Issue #5" is figuring out why you put yourself through all this pain, rather than just writing your CRUD interface by hand with Spring Web MVC (or even vanilla JSP's) in the first place! :)
Support for using SYS_GUID() is dependent upon the Oracle dialect that you are using. Looking at the hibernate source on GitHub it appears that the dialect was only setup to use the Oracle-generated guid in Oracle9Dialect.java and Oracle8iDialect.java. Therefore, it won't work with the 9i or 10g dialects.
You should submit a patch to hibernate which will add the required function(s) to enable the same functionality as the other dialects.

Identity Property in Sync Services

can someone help me understand the identity property on an entity attribute? Im thinking of the identity property as a table "key" as in a Relational Database, but I'm guessing this is not it.
Im using core data and in my entities I have not defined any "Key" columns, and all is working fine.
But now that I have added sync services to my app, Im not sure how to use this sync attribute. My app is a task management planner, so I have an entity called task. I have an attribute called "name" , "due date" and other optional attributes. If I have 2 tasks with the same name, I want them both sync, so Im guessing the identity property wont do me good if I set it on the attribute "name". Right now I have not used it, and sync services is working fine...
So my question is, in what other scenarios should I use the identity property?
Are you using the standard definition of a task entity from Sync Services? Apple defined one fairly well:
http://developer.apple.com/library/mac/#documentation/AppleApplications/Reference/SyncServicesSchemaRef/Articles/Calendars.html
If so, there are three identity properties (summary, record id and calendar).
If you're defining your own entity (not sharing with the system entity) you can make additional attributes identity properties - I'd probably go with "name" and "due date" for sure; you could also add in a "created date" to ensure that you are able to truly create a unique value to compare records using.
In my experience, however, Sync Services has done a good job keeping track of tasks with the identity properties of the system's schema - I can't remember the last time I saw duplicate tasks coming through my own or user's data.
Tell me more about why you might not want to use the system schema but instead roll your own - I can think of pros and cons to each.

MS CRM 4 - Custom entity with "regardingobjectid" functionality

I've made a custom entity that will work as an data modification audit (any entity modified will trigger creating an instance of this entity). So far I have the plugin working fine (tracking old and new versions of properties changed).
I'd like to also keep track of what entity this is related to. At first I added a N:1 from DataHistory to Task (eg.) and I can indeed link back to the original task (via a "new_tasksid" attribute I added to DataHistory).
The problem is every entity I want to log will need a separate attribute id (and an additional entry in the form!)
Looking at how phone, task, etc utilize a "regardingobjectid", this is what I should do. Unfortunately, when I try to add a "dataobjectid" and map it to eg Task and PhoneCall, it complains (on the second save), that the reference needs to be unique. How does the CRM get around this and can I emulate it?
You could create your generic "dataobjectid" field, but make it a text field and store the guid of the object there. You would lose the native grids for looking at the audit records, and you wouldn't be able to join these entities through advanced find, fetch or query expressions, but if that's not important, then you can whip up an ASPX page that displays the audit logs for that record in whatever format you choose and avoid making new relationships for every entity you want to audit.
CRM has a special lookup type that can lookup to many entity types. That functionality isn't available to us customizers, unfortunately. Your best bet is to add each relationship that could be regarding and hide the lookups that aren't in use for this particular entity.

Resources