find All Models like this:
$models=Termin::model()->findAll(
array("condition"=>"status = 3"));
and than set new status with foreach:
foreach($models as $data) {
$data->status=1;
if($data->save())
{
echo 'saved';
}
}
but nothing happens ;(
Correction:
From Yii 1.1 onwards you can use save() for both update and save operations. CActiveRecord (your model's parent class) automatically determines if the current model ($data in your case) is a fresh record or it was retrieved from a db (with $data->isNewRecord property). If the record is fresh, CActiveRecord class will automatically use insert() method and if it was retrieved from an existing recordset it will switch to update().
Your question: save() method (from CActiveRecord parent class) have two optional parameters. One to run validations (as set in your model rules) before actually sending to the db handler for saving, and the other to specify which attributes you want to save. By default this validation is set to true - meaning: plain $data->save() method will run all your model level validations. If the validations fail, there would not be any attempt at writing the record, but instead will add error details to the model. You can fetch these errors by calling $data->getErrors() method on the model. (Note that getErrors() is from yet another parent class CModel)
If you call $data->save(false), model validations [defined in your model's rules()] will not be run and the record will be passed on for the db handler for writing. This will come back with a success (or an exception, if there were any errors at the db level - e.g. foreign key / unique / other).
Hence, I would refactor your code as follows:
foreach($models as $data) {
$data->status=1;
try { // try catch block to capture db level mishaps
if ($data->save(true)) { // run model validations and if success
echo 'saved';
} else { // model validations failed
// Debug with var_dump($data->getErrors()) or Yii's CVarDumper class
// And/Or Display error message to user
}
} catch (CException $ex) { // This will catch any errors thrown during db write
// Debug with var_dump($ex)
// And/Or Display error message to user
}
}
Found the Solution by my own.
In yii 1.1 its update() instead of save(). In yii 2 you can always use save() i thought!
Related
I was using a grails CommandObject to store all the information for a search page. Something Like
class SomeSearchCommand {
User user
...
Integer max=100
Integer offset=0
}
And then in the controller
class SomeThingController {
def search(SomeSearchCommand c) {
// build a query based SomeSearchCommand
Something.where {
if (c.user) user==c.user
...
}.list(max: c.max, offset: c.offset)
}
}
This appears to work well, and when you call the action with
/someThing/search?user=3&offset=100
It finds all the "SomeThing" objects that belong to user 3.
But while playing around with the code I found out that if you change the url to
/someThing/search?user.id=3&user.name=ichangedyourname&offset=100
Then the User object inside of the domain object would be loaded with id=3 from the database, and the user.name property would be changed. The User object is now marked as dirty and has been added to the session. If you happen to execute some code that calls a flush (or have flushMode set to Auto), then the change to the user object is persisted.
I found a workaround by adding a #BindUsing around the user object. This seems to disable the deep binding on the user object and only allows binding by the parameter of user and not user.id
class SomeSearchCommand {
#BindUsing({obj, source ->
if (source['user'] instanceOf User) return source['user']
if (source['user'] instanceOf serializable) return User.get(source['user'])
})
User user
...
Integer max=100
Integer offset=0
}
Another workaround is to just not use the User Domain Object in the CommandObject.
class SomeSearchCommand {
String userId
User getUser() {
return User.get(userId)
}
...
Integer max=100
Integer offset=0
}
This means the URL request has to change to /some/search?userId=3&offset=100
But user is still a property on the command object that returns the typed object.
I first noticed this using grails 2.3.11 (where flushMode is set to auto), so that the inner domain object was always being written to the database.
In grails version after 2.4 the flush mode is set to Manual, so the modified domain object only gets persisted of the session is flushed.
I don't see any examples in the grails docs about putting a domainObject inside a commandObject. It always seems to be basic types.
I retrieved the domain object using read(), changed some properties on it and explicitly saved it using save(). I can see version being updated but not properties
Properties should be updated along with the version as mentioned in grails read() documentation
Example Domain:
class AdhocChargeType{
String adHocChargeType
String glCode
}
Controller test method:
class AdhocChargeTypeController
{
def testRead(Long id)
{
AdhocChargeType adHocChargeType = AdhocChargeType.read(id)
adHocChargeType.properties = [adHocChargeType:"changed?"]
adHocChargeType.save()
}
}
Grails save() method silently fails. If you have validation errors, then it will not save.
try with
save flush:true, failOnError:true
Resolve if there is any validation errors. The data should persist after that.
Upon saving a domain instance, Grails validates it using the defined constraints. The save will fail, if values in the domain instance violate those constraints. The problem is, this failure occurs quietly: you only know about it if you check the return value of save() or call hasErrors().
if (!adHocChargeType.save()) {
// Save failed! Present the errors to the user.
...
}
Another approach is :
adHocChargeType.save(failOnError: true)
I suggest have a look into GORM best practices:
http://spring.io/blog/2010/06/23/gorm-gotchas-part-1/
http://spring.io/blog/2010/07/02/gorm-gotchas-part-2/
http://spring.io/blog/2010/07/28/gorm-gotchas-part-3/
I'm using a customized method for tracking individual modified properties of an n-tier disconnected entity class. I extracted it from
Programming Entity Framework: DbContext by Julia Lerman and Rowan
Miller (O’Reilly). Copyright 2012 Julia Lerman and Rowan Miller,
978-1-449-31296-1.
The code is:
public void ApplyChanges<TEntity>(TEntity root) where TEntity : class, IObjectWithState {
// bind the entity back into the context
dbContext.Set<TEntity>().Add(root);
// throw exception if entity does not implement IObjectWithState
CheckForEntitiesWithoutStateInterface(dbContext);
foreach (var entry in dbContext.ChangeTracker.Entries<IObjectWithState>()) {
IObjectWithState stateInfo = entry.Entity;
if (stateInfo.State == RecordState.Modified) {
// revert the Modified state of the entity
entry.State = EntityState.Unchanged;
foreach (var property in stateInfo.ModifiedProperties) {
// mark only the desired fields as modified
entry.Property(property).IsModified = true;
}
} else {
entry.State = ConvertState(stateInfo.State);
}
}
dbContext.SaveChanges();
}
The purpose of this method is to let the EF know only a predefined set of entity fields are ready for update in the next call of SaveChanges(). This is needed in order to workaround the entity works in ASP.NET MVC 3 as follows:
on initial page load: the Get action of the controller is loading the
entity object and passing it as a parameter to the view.
The View generate controls for editing 2 of the fields of the entity,
and holds the ID of the record in a hidden field.
When hitting [save] and posting the entity back to the controller all
of the fields excepting the 3 preserved in the view comes with a null
value. This is the default behavior of the MVC binding manager.
If i save the changes back to the database the update query will of course overwrite the non mapped fields with a sentence as follows:
UPDATE non_mapped_field_1 = NULL, ..., mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2', ... non_mapped_field_n = NULL WHERE ID = mapped_field_3
This is the reason i'm trying to track the fields individually and update only those fields i'm interested in. before calling the custom method with ApplyChanges() i'm adding the list of fields i want to be included in the update to the IObjectWithState.ModifiedProperties list, in order to get a SQL statement as follows:
UPDATE mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2' WHERE id = mapped_value_3
The problem is, when marking one of the fields as modified in ApplyChanges, i.e.:
entry.Property(property).IsModified = true;
the system is throwing the following exception:
{System.InvalidOperationException: Member 'IsModified' cannot be called for property 'NotifyCEDeadline' on entity of type 'User' because the property is not part of the Entity Data Model.
at System.Data.Entity.Internal.InternalPropertyEntry.ValidateNotDetachedAndInModel(String method)
at System.Data.Entity.Internal.InternalPropertyEntry.set_IsModified(Boolean value)
at System.Data.Entity.Infrastructure.DbPropertyEntry.set_IsModified(Boolean value)
...
So the question is. There's a way to bypass this EF validation or let the context know of the existance of this system property (IsModified) that i'm trying to change?
Summary of the architeture:
EF Code first (annotation + Fluent API)
Oracle .NET EF Data provider (ODAC)
Context is injected to a cutom business context with nInject.MVC => this is the reason i customized the ApplyChanges() method from
using (var context = new BreakAwayContext()){
context.Set().Add(root);
to a simple call to the already initialized dbcontext
dbContext.Set().Add(root);
Oracle Database is created manually i.e. without the help of EF, so no EF metadata tables are used.
Thanks,
Ivan.
Very good description, however I can't find any information on why you need a transient property called "IsModified" in the object and/or why you need to tell EF about it being modified (EF won't be able to persist it anyway).
The value of the IsModified property should be set by the model binder if the property was incldued in the view anyway.
You could just add code in your ApplyChanges method to skip a property named "IsModified", or even better, filter only known properties using entry.CurrentValues.PropertyNames, e.g.:
foreach (var property in stateInfo.ModifiedProperties) {
// mark only the desired fields as modified
if (entry.CurrentValues.PropertyNames.Contains(property)) {
entry.Property(property).IsModified = true;
}
}
Update: Ivan, very sorry I did not understand the problem better when you posted it several months ago and that I did not follow up after your added these clarifying comments. I think I understand better now. That said, I think the code snippet that I offered can be part of the solution. From looking at the exception you are getting again, I understand now that the problem that EF is detecting is that NotifyCEDDealine is not a persistent property (i.e. it is not mapped in the Code First model to a column in the database). IsModified can only be used against mapped properties, therefore you have two options: you change the code of the implementation of IObjectWithState in your entities so that non-mapped properties are not recorded in ModifiedProperties, or you use my code snippet to prevent calling IsModified with those.
By the way, an alternative to doing all this is to use the Controller.TryUpdateModel API to set only the modified properties in your entities.
Hope this helps (although I understand it is very late).
How do I disable Model validation for a single Action in a Controller ?
Or can I do it per model by registering the model type at startup somewhere ?
I want the ModelBinder to bind to the model, but afterwards it should not perform the model validation.
The reason why i dont want validation to happen is because i am trying to move the logic from the controllers to a service layer which will be responsible for validating the models as i cant assume that models being passed to a service contains valid data.
As I understand this is the recommend approach (to not have logic in the controllers), so I find it a bit strange that i cant seem to find anything about how the model validation can be disabled (per action or per model type).
Please notice that I dont want to disable model validation for the entire webapplication (by removing the validation providers), and i dont want to disable the input validation that checks for malicious code being posted.
UPDATE
I am using .Net 4.0 and MVC 3 Preview 1
Just remove the items you don´t need before checking if the model is valid
ModelState.Remove("Email");
if (ModelState.IsValid)
{
// your logic
}
I've solved this problem with this code:
public ActionResult Totals(MyModel model)
{
ModelState.Clear();
return View(model);
}
Not sure it's the right way though.
Unfortunately there seems to be no easy way to disable the model validation happening in the ModelBinder except for registering every single model type you don’t want to validate (including nested complex types) with a specific ModelBinder. It can be done with the following code:
ModelBinders.Binders[typeof(MyModelType)] = new NonValidatingModelBinder();
Creating a SkipValidationAttribute that can be attached to action methods or action method parameters doesn’t seem possible as it should be implemented in the ControllerActionInvoker, but there is no way of letting the ModelBinder know that it should do any validation in the SetProperty() and OnModelUpdated methods when calling BindModel() in the GetParameterValue() method.
I definitely dislike this addition in the 2.0 version, because, as you stated in your question, validation makes more sense in the Service layer. In this way you can reuse it in other non-web applications, and test it more easily without having to mock the auto-validation mechanism.
Validation in Controller layer is pointless because in this part you can only verify model data and not business rules. For example, think of a service responsible of adding new comments and a user that wants to post a new one, the data in the comment he/she is posting may be valid, but what happens if the user is banned to comment because of misbehavior in the past? You should do some validation in the Service layer to ensure this is not happening, and if it does, throwing an exception. In short, validation must be done in the Service layer.
I use xVal as my Validation framework because it's compatible with the DataAnnotationModel, allows me to place validation wherever I want and performs client-side validation without extra-effort, even remote-client side. This is how I use it at the beginning of each of my services, for example, a login service:
public void SignIn(Login login) {
var loginErrors = DataAnnotationsValidationRunner.GetErrors(login);
// Model validation: Empty fields?
if (loginErrors.Any())
throw new RulesException(loginErrors);
// Business validation: Does the user exist? Is the password correct?
var user = this._userRepository.GetUserByEmail(login.Email);
if (user == null || user.Password != login.Password)
throw new RulesException(null, "Username or password invalids");
// Other login stuff...
}
Simple, web-independent and easy... then, in the Controller:
public RedirectResult Login(Login login) {
// Login the user
try {
this._authenticationRepository.SignIn(login);
} catch (RulesException e) {
e.AddModelStateErrors(base.ModelState, "Login");
}
// Redirect
if (base.ModelState.IsValid)
return base.Redirect(base.Url.Action("Home"));
else return base.Redirect(base.Url.Action("Login"));
}
I would recommend you perform validation in both places rather than trying to turn off validation in the UI. I understand your point that the service cannot assume that it's being passed valid data - that is a valid concern and is the reason your service should have validation. But you should also have validation in your UI. This is also nice because you can have client-side validation to prevent user errors and give you users a better experience.
I know that this already been answered but what you really needed was to extend the DataAnnotationsValidatorProvider and override the GetValidators method.
Then, on startup, you would remove the DataAnnotationsValidatorProvider from ModelValidatorProviders.Providers and add your new one.
Needless to say, if you simply don't want any validation at all, you can simply have the GetValidators method returning an empty collection.
In my case, I need to remove validation only when submitting the forms while still keeping the client-side validation, hence the following:
public class DynamicModelValidatorProvider : DataAnnotationsModelValidatorProvider
{
GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
{
if (context.HttpContext.Request.HttpMethod == "POST")
{
return new ModelValidator[] { };
}
return base.GetValidators(metadata, context, attributes);
}
}
I use
[ ValidateInput( false )]
Not sure if this prevents model validation, or only IIS submit validation.
I'm using data annotations to check data that's being entered, but I'm stuck when it comes to more custom way to validate data.
I need to run queries against database to see if stuff exists there or not, and then report back to user if a "custom db-check error" appears, such as "The Companyname already exists"
How can I implement such a thing together with dataannotations?
I have all the queries done etc using linq and entity framework that comes with 3.5sp1
/M
Custom attributes that extend data annotations
You will have to write your own attributes that will do the validation of your object instance against data store.
Make sure your classes inherit System.ComponentModel.DataAnnotations.ValidationAttribute class:
public class MustNotExist: ValidationAttribute
{
...
}
Caution
I've run into a similar situation when I needed to validate that the object is unique within data store. But this kind of validation wasn't possible on the entity class itself, since it should only work for those entities that are being created but not when you return your entity from the data store already.
My solution was to have a separate interface, class and attribute.
public interface IExternalValidator ...
class DBUniqueValidator: IExternalValidator ...
class ValidateExternallyAttribute: FilterAttribute, IActionFilter
{
...
public ValidateExternallyAttribute(Type validatorType, Type entityType) ...
...
}
I was able to place my attribute on controller actions that get entity parameters. Filter action attribute then checks controller action parameters (it can easily access their types and values) and runs external validator against correct parameters (provided types in attribute definition) and populates ModelState errors when validation fails.
[ValidateExternally(typeof(DBUniqueValidator), typeof(User))]
public ActionResult RegisterUser(User newUser)
{
if (!this.ModelState.IsValid)
{
// act accordingly - probably return some error depending on model state errors
}
// register new user in data store
}
This way I was able to run external validation only on those actions that actually needed it, and this technique also helped my controller actions code to stay clean and short. All I had to do is to check if there are any model state errors.