How to apply validation to one field if other field is empty using FluentValidation? - fluentvalidation

I have never heard of FluentValidation until today while I was working on a project, so I have run into an issue. I have this..
RuleFor(x=>x.Company)
.NotEmpty()
.WithMessage("Company Required");
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage(FirstName Required");
RuleFor(x => x.LastName)
.NotEmpty()
.WithMessage("LastName Required");
and a bunch of other RuleFor statements.What I need to do is..
If Company field is empty then require validation for FirstName and LastName, but if Company field is not empty then don't apply validation to FirstName and LastName
I have no idea where to start.
EDIT
I tried the When condition and came up with this
When(x => x.Company == "" || x.Company == null, () =>
{
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage("FirstName Required");
RuleFor(x => x.LastName)
.NotEmpty()
.WithMessage("LastName Required");
});
and I "think" that should have set off the validation for FirstName and LastName, but it didn't.
Then I tried this way
When(x.Company.length == 0, () =>
{
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage("FirstName Required");
RuleFor(x => x.LastName)
.NotEmpty()
.WithMessage("LastName Required");
});
and the same thing happened, the FirstName and LastName validation didn't happen.

Your first attempt at using the When statement should have worked. This is what worked for me.
When(x => string.IsNullOrWhiteSpace(x.Company), () => {
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage("{PropertyName} Required");
RuleFor(x => x.LastName)
.NotEmpty()
.WithMessage("{PropertyName} Required");
});

Related

Best way to shorten store method in controller

I'm trying to shorten my controller code, and I want to know the conventions to use with Laravel while validating and storing.
Controller
public function store(Request $request)
{
// Validation
$user_id = Auth::user()->id;
$request->validate([
'lname' => 'required|max:255',
'fname' => 'required|max:255',
'ar_lname' => 'required|max:255',
'ar_fname' => 'required|max:255',
'tel' => 'required|digits:10|unique:infos',
'level' =>'required|max:50',
'goal' =>'required',
'img' => 'required|image|mimes:jpeg,bmp,png',
'cin' => 'required|image|mimes:jpeg,bmp,png',
]);
// Store
info::create([
'user_id' => $user_id,
'lname' => $request->lname,
'fname' => $request->fname,
'ar_fname' => $request->ar_fname,
'ar_lname' => $request->ar_lname,
'bday' => $request->bday,
'tel' => $request->tel,
'level' => $request->level,
'goal' => $request->goal,
'img' => $request->file('img')->store('images', 'public'),
'cin' => $request->file('cin')->store('cins/' . $request->lname . ' '. $request->fname ),
'registered' => true,
]);
// Redirect
return redirect()->route('user.index');
}
First of all you can isolate the validation in a dedicated class following the Laravel way by creating a custom Request with your rules.
php .\artisan make:request StoreInfoRequest
StoreInfoRequest
class StoreInfoRequest extends FormRequest
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'lname' => 'required|max:255',
'fname' => 'required|max:255',
'ar_lname' => 'required|max:255',
'ar_fname' => 'required|max:255',
'bday' => 'date',
'tel' => 'required|digits:10|unique:infos',
'level' => 'required|max:50',
'goal' => 'required',
'img' => 'required|image|mimes:jpeg,bmp,png',
'cin' => 'required|image|mimes:jpeg,bmp,png',
];
}
}
Then this can't be called a Laravel way but it will be very short and clean:
public function store(StoreInfoRequest $request)
{
info::create(
array_merge(
['user_id' => Auth::user()->id],
$request->safe()->except(['img', 'cin']),
['img' => $request->file('img')->store('images', 'public')],
['cin' => $request->file('cin')->store('cins/' . $request->lname . ' ' . $request->fname)],
['registered' => true],
)
);
return redirect()->route('user.index');
}
If you are using Laravel 9 you can do return to_route('user.index');
It is a good practice to create separate classes for each concerns, like for your controller it should only handle receiving and returning the output of your http request.
So you should create classes for the FF:
Class that will handle the validation
Class that you will handle the business logic
As what Medilies answered you have to create a separate file for validating all incoming data. You need to create a Request file that will handle it.
php artisan make:request StoreInfoRequest
StoreInfoRequest
class StoreInfoRequest extends FormRequest
public function authorize(): bool
{
return true;
}
public function rules(): array
{
// declare here everything even it is not required
return [
'lname' => 'required|max:255',
'fname' => 'required|max:255',
'bday' => 'date',
];
}
this will return an array of the validated columns you entered in your StoreInfoRequest $validated.
Then create a service file that will handle your business logic say InfoService. Within this file you can do the eloquent saving. By then you can have clean and thin controller like this.
public function store(StoreInfoRequest $request)
{
$this->InfoService->store($request->$validated);
return redirect()->route('user.index');
}
Don't forget to instantiate the service file in your controller's __constructor method.
public function __constructor(StoreInfoRequest $storeInfoRequest)
{
$this->storeInfoRequest = $storeInfoRequest;
}

Kendo Grid ClientTemplate conditional column

I am working with ASP.NET MVC 4 with Kendo UI(kendo grid).Below is sample code of Kendo Grid -
#(Html.Kendo().Grid(Model.Users).Name("Grid").Columns(columns =>
{
columns.Bound(p => p.FirstName);
columns.Bound(p => p.LastName);
columns.Bound(p => p.UserName);
columns.Bound(p => p.Email);
columns.Bound(o => o.IsActive).ClientTemplate(links).Title("Action");
})
In the above code my IsActive column have some links for Actions like Edit,Update,Delete.And i am adding those links into Kendo grid by links variable.And I want to use links variable on the basis of conditions.Means i want conditional ClientTemplate here.
So anyone suggest how can make a conditional ClientTemplate in kendoGrid ?
2) Also i want to add condition on the basis on the bool field value of my model(Model.Users).
So i want to know how we can get that field from Model.Users model in kendo grid for each row.Like -
.ClientTemplate(if(IsAdmin && ViewBag.IsActive){.....} else{....})
You can try like below code..may be this help you..
columns.Bound(p => p.Active).ClientTemplate("\\#if('#=Active#'=='Y') {\\<input type='button' value='OK' />\\}\\#");
or may be use
"#= (Active) ? ' ' : 'your code here' #"
You can use the following piece of code:
#(Html.Kendo().Grid(Model.Users).Name("Grid").Columns(columns =>
{
columns.Bound(p => p.FirstName);
columns.Bound(p => p.LastName);
columns.Bound(p => p.UserName);
columns.Bound(p => p.Email);
columns.Bound(o => o.IsActive).ClientTemplate("#if(IsActive){#<a href='javascript:void(0)' >Edit</a>#}#").Title("Action");
})
I'm concatenating a name and using a javascript function which made condition testing much easier, plus you can get access to multiple fields:
cshtml:
#(Html.Kendo().Grid<Debtors>()
.Name("Debtors")
.Columns(columns =>
{
columns.Bound(c => c).Title("Name").ClientTemplate("#=showName(data)#");
columns.Bound(c => c.Busname);
...
})
...
)
js:
function showName(data) {
var returnName = "";
if (data.Lname) {
returnName = data.Lname;
if (data.Fname) {
returnName += ", " + data.Fname;
if (data.Mi) {
returnName += " " + data.Mi;
}
}
}
return returnName;
}

Breeze: Differences between Entity Framework and NHibernate with many to many

Here is the situation:
WebApi v1
Breeze 1.4.7
EF 5.0/NHibernate 3.3.1
What We want: A many to many exposed as a many to one. A client can have multiple countries and a country can have multiple clients. A ClientCountry entity has been created for that purpose.
My mapping looks like this:
Entity Framework:
modelBuilder.Entity<Client>().HasKey(p => p.Id).Property(p=>p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Client>().Property(p => p.Abbreviation);
modelBuilder.Entity<Client>().Property(p => p.ClientSinceDate).IsRequired();
modelBuilder.Entity<Client>().Property(p => p.ClientUntilDate);
modelBuilder.Entity<Client>().Property(p => p.Name).IsRequired();
modelBuilder.Entity<Client>().Property(p => p.Website);
modelBuilder.Entity<Client>().HasMany(p => p.Contacts).WithRequired(p => p.Client).WillCascadeOnDelete(true);
modelBuilder.Entity<Client>().HasMany(p => p.ClientCountries).WithRequired(p => p.Client).WillCascadeOnDelete(true);
modelBuilder.Entity<Contact>().HasKey(p => p.Id).Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Contact>().Property(p => p.Username);
modelBuilder.Entity<Contact>().HasRequired(p => p.Client);
modelBuilder.Entity<Country>().HasKey(p => p.Id).Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Country>().Property(p => p.ValidFrom);
modelBuilder.Entity<Country>().Property(p => p.ValidTo);
modelBuilder.Entity<Country>().Property(p => p.Code);
modelBuilder.Entity<Country>().Property(p => p.DefaultLabel);
modelBuilder.Entity<Country>().Property(p => p.Description);
modelBuilder.Entity<Country>().Property(p => p.DisplayOrder);
modelBuilder.Entity<ClientCountry>().HasKey(p => p.Id).Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<ClientCountry>().Property(p => p.ValidFrom);
modelBuilder.Entity<ClientCountry>().Property(p => p.ValidTo);
modelBuilder.Entity<ClientCountry>().HasRequired(p => p.Client);
modelBuilder.Entity<ClientCountry>().HasRequired(p => p.Country);
NHibernate:
public class BaseMapping<T> : ClassMapping<T> where T : BaseEntity
{
public BaseMapping()
{
this.Lazy(true);
Id(x => x.Id, map => { map.Generator(Generators.GuidComb); });
}
}
public class ClientMap : BaseMapping<Client>
{
public ClientMap()
{
this.Property(x => x.Name);
this.Property(x => x.Abbreviation);
this.Property(x => x.ClientSinceDate, map => map.NotNullable(true));
this.Property(x => x.ClientUntilDate);
this.Property(x => x.City);
this.Property(x => x.Website);
this.Bag<Department>(x => x.Departments, colmap =>
{
colmap.Key(x => x.Column("ClientId"));
colmap.Inverse(true);
colmap.Cascade(Cascade.All | Cascade.DeleteOrphans);
}, map =>
{
map.OneToMany();
});
this.Bag<ClientCountry>(x => x.ClientCountries, colmap =>
{
colmap.Cascade(Cascade.All | Cascade.DeleteOrphans);
colmap.Key(p => p.Column("ClientId"));
colmap.Inverse(true);
}, map =>
{
map.OneToMany();
});
this.Bag<Contact>(x => x.Contacts, colmap =>
{
colmap.Key(x => x.Column("ClientId"));
colmap.Cascade(Cascade.All | Cascade.DeleteOrphans);
}, map =>
{
map.OneToMany();
});
}
}
public class CountryMap : BusinessRefEntityMapping<Country>
{
public CountryMap()
{
Bag<ClientCountry>(x => x.ClientCountries, colmap =>
{
colmap.Cascade(Cascade.All);
colmap.Key(p => p.Column("CountryId"));
}, map =>
{
map.OneToMany();
});
}
}
public class ClientCountryMap : BaseMapping<ClientCountry>
{
public ClientCountryMap()
{
Property(x => x.ValidFrom);
Property(x => x.ValidTo);
Property(x => x.ClientId, map =>
{
map.Column("ClientId");
map.Insert(false);
map.Update(false);
map.NotNullable(true);
});
Property(x => x.CountryId, map =>
{
map.Column("CountryId");
map.Insert(false);
map.Update(false);
map.NotNullable(true);
});
ManyToOne<Client>(x => x.Client, map =>
{
map.Column("ClientId");
map.Cascade(Cascade.All);
map.Insert(true);
map.Update(true);
map.NotNullable(true);
});
ManyToOne<Country>(x => x.Country, map =>
{
map.Column("CountryId");
map.Cascade(Cascade.All);
map.Insert(true);
map.Update(true);
map.NotNullable(true);
});
}
}
The js code:
$scope.create = function (index) {
var c = $scope.clients[index];
var newClientCountry = breezeService.manager.createEntity('ClientCountry', {
ValidFrom: Date(2013, 01, 01),
ValidTo: Date(2015, 01, 01),
Client: c,
Country: country,
});
breezeService.manager.saveChanges()
.then(function (data) {
$log.info('client created');
})
.fail(function (dat) {
$log.error('save client failed:' + data)
})
}
The issue: With NHibernate, saving a clientcountry results in this error message :"not-null property references a null or transient value CdT.EAI.DAL.ClientCountry.Country". With EF, all works as expected.
Is there something wrong with my code?
So, since there is no feedback yet, here's what we did to make it (more or less) work:
First, to be able to save a new ClientCountry(many to many with exposed junction table), we must do this:
public class ClientCountryMap : BaseMapping<ClientCountry>
{
public ClientCountryMap()
{
Property(x => x.ValidFrom);
Property(x => x.ValidTo);
Property(x => x.ClientId, map =>
{
map.Column("ClientId");
map.Insert(true);
map.Update(true);
map.NotNullable(true);
});
Property(x => x.CountryId, map =>
{
map.Column("CountryId");
map.Insert(true);
map.Update(true);
map.NotNullable(true);
});
ManyToOne<Client>(x => x.Client, map =>
{
map.Column("ClientId");
map.Cascade(Cascade.All);
map.Insert(false);
map.Update(false);
map.NotNullable(true);
});
ManyToOne<Country>(x => x.Country, map =>
{
map.Column("CountryId");
map.Cascade(Cascade.All);
map.Insert(false);
map.Update(false);
map.NotNullable(true);
});
}
}
The difference here is that the insert/update are inverted. Insert(true) and Update(true) must be set on the foreign key mapping and not the association. It differs from the breeze doc on that point.
The second is that Inverse(true) must be set on each collection association or all your collections will be deleted when updating the parent entity.
With that,changes, it seems to work as expected.
ps: It is fixed in the latest version (>1.4.8). The comments above don't apply anymore.

nhibernate queryover how to select an alias column?

Assuming I have three simple tables
schedule
{
Student student { get; set;}
Teacher teacher { get; set;}
bool Deleted { get; set; }
}
Student
{
string Name { get; set; }
IList<schedule> TeacherMeetings {get; set; }
}
and assume teacher has the same thing, name and list of student schedules.
I want to select, from schedule, the list of all student names for a particular teacher. I can write the query for conditions and everything, but having trouble selecting just the student names.
Here is my current query:
DetachedCriteria dc = QueryOver.Of<Schedule>(() => sAlias)
.JoinAlias(() => sAlias.student, () => studentAlias)
.JoinAlias(() => sAlias.teacher, () => teacherAlias)
.Where(() => teacherAlias.MembershipGuid == teacherGuid)
.AndNot(() => sAlias.isDeleted)
//.Select(() => studentAlias.Name)
.SelectList(list => list
.SelectGroup(x => x.student.Name).WithAlias(() => studentAlias.Name))
.DetachedCriteria
;
If I comment out all select, profiler shows the query being generated as the right one, with right joins on right tables, only, it selects all columns on all tables.
I can't seem to get the select right, I only need the student.Name only.
How do I write this please? I've been at this for over an hour trying out different things but it is always the select that errors out, saying NHibernate.QueryException: could not resolve property and that studentName isn't recognizable.
thanks.
If you just want a list of strings, you shouldn't need to use WithAlias. WithAlias is used to project that column into a member on a result object.
Something like this should work:
DetachedCriteria dc = QueryOver.Of<Schedule>(() => sAlias)
.JoinAlias(() => sAlias.student, () => studentAlias)
.JoinAlias(() => sAlias.teacher, () => teacherAlias)
.Where(() => teacherAlias.MembershipGuid == teacherGuid)
.AndNot(() => sAlias.isDeleted)
.SelectList(list => list
.Select(() => studentAlias.Name))
.DetachedCriteria;
So the following should get you a list of strings:
IList<string> names = dc.GetExecutableCriteria(session)
.List<string>();
Update (per comment). Here's how you would order by student name, descending:
DetachedCriteria dc = QueryOver.Of<Schedule>(() => sAlias)
.JoinAlias(() => sAlias.student, () => studentAlias)
.JoinAlias(() => sAlias.teacher, () => teacherAlias)
.Where(() => teacherAlias.MembershipGuid == teacherGuid)
.AndNot(() => sAlias.isDeleted)
.SelectList(list => list
.Select(() => studentAlias.Name))
.OrderBy(() => studentAlias.Name).Desc()
.DetachedCriteria;

Mapping nested components in Fluent NHibernate

I have a 'User' Entity that contains an 'Address' Value Object. I have this mapping ok using FNH's Component concept. However, the Address VO also contains a Country which is another value object. I had assumed that this should be just nested as another component, but this doesn't seem to work. Can anyone tell me how I should solve this?
Code for mapping is below...
Thanks!
public UserMapping()
{
Table("Users");
Id(c => c.Id).GeneratedBy.HiLo("100");
Map(c => c.UserName).Not.Nullable().Length(64);
Map(c => c.Email).Not.Nullable().Length(128);
Map(c => c.Password).Not.Nullable().Length(256);
Map(c => c.Roles).Length(64);
Map(c => c.FirstName).Not.Nullable().Length(64);
Map(c => c.LastName).Not.Nullable().Length(64);
Map(c => c.BirthDate).Not.Nullable();
//Address
Component(x => x.Address, m =>
{
m.Map(x => x.AddressLine1).Not.Nullable();
m.Map(x => x.AddressLine2);
m.Map(x => x.City).Not.Nullable();
m.Map(x => x.Region);
m.Map(x => x.PostalCode).Not.Nullable();
//*****Country Here********
// country has Name and Code
});
}
Ah, Jimmy Bogard from the FNH mailing list showed me - it's quite straightforward. I don't know what I was doing before! Anyway, for anyone else who's interested:
Component(c => c.Address, m =>
{
m.Component(cp => cp.Country, m2 =>
{
m2.Map(x => x.Name); //etc
}
I would create a map for Country and use m.References(x => x.Country).