I propose building a better Validation system with built-in validations rules and built-in i18n supoort. For example:
class Person < ApplicationRecord
validates :name, :login, :email, presence: true
validates :name, length: { minimum: 2 }
validates :bio, length: { maximum: 500 }
validates :password, length: { in: 6..20 }
validates :registration_number, length: { is: 6 }
end
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false
Rails have validation helpers for: acceptance, validates_associated, confirmation, exclusion, format, inclusion, length, numericality, presence, absence, uniqueness ..etc!
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
Laravel provides a lot of built-in validation rules!
https://laravel.com/docs/5.6/validation#available-validation-rules

Finally, I'm a beginner at Crystal and Ruby and came from Laravel/PHP background, I don't know If I can help in participating in re-implement Amber Validation system, but I will try to do my best to help you guys!
Useful links to check:
Rails:
Rails Validation documentation
Rails Validation implementation:
http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html
https://github.com/rails/rails/blob/cdb9d7f481a6b299cd53a0f647397da60d806a21/activemodel/lib/active_model/validations.rb
https://github.com/rails/rails/blob/cdb9d7f481a6b299cd53a0f647397da60d806a21/activemodel/lib/active_model/validations/validates.rb
https://github.com/rails/rails/blob/d57356bd5ad0d64ed3fb530d722f32107ea60cdf/activemodel/lib/active_model/validations/with.rb
Laravel
Laravel Validation documentation
Laravel Validation rules
Laravel Validation implementation
Hi @diaafares , Thank you for reporting this issue!
I think our validators should be consistent between amber framework and granite orm
WDYT about using something similar to Granite validators?
With granite validators we already can use something like this:
class Person < Granite::ORM::Base
adapter pg
field name : String
validate :name, "cannot be blank" do |this|
!this.email.blank?
end
end
person = Person.new
person.name = ""
person.valid? # false
person.name = "Jon Doe"
person.valid? # true
@amberframework/contributors WDYT?
@diaafares thank you for submitting the issue.
The framework has params and model validations and it provides a set of methods to validate strings, numbers via extensions https://github.com/docelic/amber-introduction#extensions while not the same these are similar to the Laravel reference.
See Params https://github.com/docelic/amber-introduction#parameter_validation
Let me know your thoughts.
@faustinoaq I know you have mentioned a few times to have the Params and Granite validations in similar way. I defer on this thought Granite is a ORM shard and the validation act on database fields params are very different from a Database field.
I defer on this thought Granite is a ORM shard and the validation act on database fields params are very different from a Database field.
Ok, I agree, Fair enough :+1:
@faustinoaq @eliasjpr Thank you guys for your quick response and your big efforts, I really like how the Amber framework team is very kind with the community 馃憤
About Parameter Validation and Granite validations, I feel like the syntax is not beautiful like Rails and could be better, For example take a look at this:
Amber Granite validations Code:
validate :name, "cannot be blank" do |this|
!this.name.blank?
end
Rails Code:
validates :name, presence: true
# Or Multiple fields at one line:
validates :name, :login, :email, presence: true
The Rails code is shorter and does not need use blocks and you can check multiple fields in one line, I really hope the we can use the shorter syntax like Rails in Amber, and instead of using "extensions" we could have "validation rules" which have a more clearer name and easier to use.
Or Maybe Amber Granite validations should look like this, write validation rules when you define your fields:
class Person < Granite::ORM::Base
adapter pg
field name : String, presence: true, length: { minimum: 6 }
field email: String, presence: false, confirmation: true
end
Another Example, Parameter Validation:
def index
params.validation do
required(:name) { |n| n.size > 6 } # Name must have at least 6 characters
optional(:phone) { |n| n.phone? } # Phone must look like a phone number
end
I feel like it will be better syntax to make it like this:
def index
params.validation do
validates :name, presence: true, length: { minimum: 6 }
validates :phone, presence: false, phone
end
another question, is Amber support i18n in error messages?
Thank you again for being very responsive and kind with newbies :)
@diaafares Ok, Rails validation syntax looks simpler :+1:
I think to make amber looks like that we should implement another layer on top of our validation system (both amber params and granite models)
@eliasjpr WDYT?
I remember we already opened some issue/PR related to this topic (I can't found it :sweat_smile: )
We should create a validations shard that can be use for model and params this way we have a single API for validations.
@eliasjpr Great idea!
And If the new new validation system support built-in validation rules it will be even better, then I hope that I will help you to port some of Laravel validation rules when the new validation system arrive!
A well structured validation system will make Amber more elegant and easier to use.
I'm very excited, Is there an estimated time frame for that?
We would have to add it to the pipeline, maybe in a couple of next releases we can have it. I don't want to make an promises right now.
Any consideration of dry validations ?
@seanfcarroll I was just looking at this to start work on a validation library for amber. Here is a very very unfinished draft of the API.
class User < Some::ORM::Model
validate(self) do
required :first_name, type: String, exclusion: [], default: 0
required :last_name, type: String, inclusion: [], default:
required :email, type: Int32, format: %r{}, default:
required :password, type: Int32, length:, default: , confirm: :confirm_password
required :confirm_password, unique:, default:,
optional :field, type: Int32, length:, default:
# Either validate inline
validate( :address ) do
required(:street), type: String, length: 255
required(:city), type: String, length: 255
required(:zipcode), type: Int64, size: 5
end
# Or let the Address object run its validation
validate(address)
if something?
inclusion :field, in: Enumerable, message: ""
length :field, min:, max:, equal:
end
end
# The above will inject the following methods
def valid?
def valid!
def properties
def errors : Errors(Error)
end
Let me know your thoughts
Hi @eliasjpr, I like it. It seems pretty simple and clear. A few points:
All in all Amber is looking good. Great work!
@seanfcarroll this is very experimental and nothing has been implemented yet. To answer your questions
How do you nominate the error messages returned from an failed validation (and map to i18n)?
inclusion :field, in: Enumerable, message: "Your error message here"
# with i18n would be something like
inclusion :field, in: Enumerable, message: i18n.t("a.key.goes.here")
Would the inline validation on address override (or ignore) any existing validations on address?
Depending on the implementation. I see the inline version of address validation as a good approach to validate json/xml schemas. And the second pass thru validation very dry in principle. In general I believe one should be able to override validations since data gets validated differently depending on the context.
The validations are directly attached to the model (as in Rails etc). One thing I do like about dry is the validation schema is a separate class. Partially because you could reuse the schema (in a form or via an inbound API), but mostly because it is a separate piece of code which is extracted (leading to less code in the model).
In this case validation can live anywhere since it is its own shard so you can create a class that is not attached to a ORM model or Controller and still be able to validate. For Example
require "validations"
class UserService
include Validation
validate(self) do
required :first_name, type: String, exclusion: [], default: 0
required :last_name, type: String, inclusion: [], default:
required :email, type: Int32, format: %r{}, default:
required :password, type: Int32, length:, default: , confirm: :confirm_password
required :confirm_password, unique:, default:,
optional :field, type: Int32, length:, default:
end
end
@eliasjpr Any progress on this? I am interested how the validation library will work when a db query is needed i.e. unique
I would love to see an improved validation syntax, but would also love to see Amber adopt a single approach for validation too. Supporting both request validation (Laravel style) and model validation (Rails style) is just confusing for newcomers as there's no standard way of validating, nor a clear guide for when one approach is favoured over the other.
Of course there are pros and cons to each approach, but I think Amber would benefit from adopting a single preferred approach and pushing that.
I suppose one thing to keep in mind (in reference to #942) is that if model validation is preferred approach then we would lean much more on Granite out of the box, and not necessarily have a solution for people using Crecto or Jennifer.
RE: Was asked by @drujensen to work on a possible implementation for nested params, as well to address type safe params and other concerns. I have been working on a solution to this, conceptually calling it Contracts, just finished a working iteration, see https://github.com/eliasjpr/contracts. Note that this might be ported over to be part of Amber Framework.
Renamed to Schema-Validations instead of contracts https://github.com/eliasjpr/schema-validations
@dwightwatson Crecto isn't supported anymore. I'm not familiar with Jennifer. Could we add a note in the docs that if using an ORM other than Granite you're on your own for validations or to use param validations instead?
I am strongly would like to suggest that we use this shard by @Blacksmoke16 https://github.com/Blacksmoke16/CrSerializer/tree/master/docs
~@eliasjpr https://github.com/Blacksmoke16/assert this one :)~
~I split out the assertion features into its own shard. They will be removed from CrSerializer next release after im doing refactoring the serialization/deserializaiton features.~
EDIT: This is https://github.com/athena-framework/validator now.
See https://github.com/eliasjpr/schema as an alternative
Most helpful comment
We should create a validations shard that can be use for model and params this way we have a single API for validations.