How to validate the date input in the form for cakephp? - cakephp-3.0

I want to limit the dates that are allowed to be picked by the user if that date already exists for any other lesson.
$validator
->date('lesson_date')
->requirePresence('lesson_date', 'create')
->notEmpty('lesson_date');
this is the validator in the Model/Table/LessonsTable.php right now. But i want to be able to check the lesson table and see if any other lesson has the same date. if there isnt, then that date can be chosen, otherwise prompted to choose on the same day next subsequent weeks

Validation rules are limited to the data of the entity itself.
If you want to compare an entity to context, you need to use build rules. If would look like:
class Table
{
// ...
function buildRules($rules)
{
// Make sure the date is unique
$rules->add($rules->isUnique(['date']));
}
// ...
}
See also
Cookbook > Validation > Creating Unique Field Rules

Related

Laravel Eloquent - auto-numbering on has many relationship

I'm very much a beginner when it comes to database relationships hence what I suspect is a basic question! I have two database tables as follows:
Projects
id
company_id
name
etc...
rfis
id
project_id (foreign key is id on the Projects table above)
Number (this is the column I need help with - more below)
question
The relationships at the Model level for these tables are as follows:
Project
public function rfi()
{
return $this->hasMany('App\Rfi');
}
RFI
public function project()
{
return $this->belongsTo('App\Project');
}
What I'm trying to achieve
In the RFI table I need a system generated number or essentially a count of RFI's. Where I'm finding the difficulty is that I need the RFI number/count to start again for each project. To clarify, please see the RFI table below which I have manually created with the the 'number' how I would like it displayed (notice it resets for each new project and the count starts from there).
Any assistance would be much appreciated!
Todd
So the number field depends on the number of project_id in the RFI table. It is exactly the number of rows with project_id plus one.
So when you want to insert a new row, you calculate number based on project_id and assign it.
RFI::create([
'project_id' => $project_id,
'number' => RFI::where('project_id', $project_id)->count() + 1,
...
]);
What I understood is that you want to set the value of the "number" field to "1" if it's a new project and "increment" if it's an existing project. And you want to automate this without checking for it every time you save a new row for "RFI" table.
What you need is a mutator. It's basically a method that you will write inside the desired Model class and there you will write your own logic for saving data. Laravel will run that function automatically every time you save something. Here you will learn more about mutators.
Use this method inside the "RFI" model class.
public function setNumberAttribute($value)
{
if(this is new project)
$this->attributes['number'] = 1;
else
$this->attributes['number']++;
}
Bonus topic: while talking about mutators, there's also another type of method called accessor. It does the same thing as mutators do, but just the opposite. Mutators get called while saving data, accessors get called while fetching data.

Yii2-How to insert status column and its automatic variations if the corresponding value used by another form

although I have searched in various resources I cannot understand how to correctly insert the ‘status’ column, I will explain better.
I have two sql tables:
From the gestionepc form using dropdown, I can select the numerazionecolumn of thenumerazionitable and so far everything works without problems. However, I need to insert the “status” column on thenumerazionitable so that if I use a record in thenumerazionicolumn, itsstatus` must automatically change to “Not active” as it is already used. For my project, it is a requirement derived from the fact that I have various groups of user permissions and various authorizations.
In practice, I'm very confused about how to insert the status column (type of column, default value, storage also on the MySQL database and initialize it) and on how to make it work through the code (perhaps using afterSave and beforeUpdate).
I read Active Record from Guide Yii2 but I don’t understand.
I modify table numerazioni in this mode:
And I try this in model Numerazioni
const STATUS_INDISPONIBILE = 'Indisponibile';
const STATUS_DISPONIBILE = 'Disponibile';
public function setStatusnumerazione()
{
if (\app\models\Gestionepc::find()->where(!isEmpty('numerazioni_id'))) {
$this->statusnumerazione = self::STATUS_INDISPONIBILE;
}
else {
$this->statusnumerazione = self::STATUS_DISPONIBILE;
}
}
But not working. Thanks in advance.

Laravel: How to get counter value when inserting with UUID and Auto Increment

My models have both id and counter attributes. The id is a UUID, and the counter is an integer which is auto-incremented by the database.
Both are unique however I rely on id as the primary key. The counter is just a human-friendly name that I sometimes display to the user.
Immediately before an object is created a listener gives it a UUID. This works fine.
When the record is saved, MySQL increments the counter field. This works fine except that the copy of the object which I have in memory does not have the counter value. I can reload the object to find out what its counter is, but that would require another database query.
Is there a way to find the value of the counter without a specific database query? For example, is it returned as part of the response from the database when a record is created?
Few things:
Use create(array $attributes) and you'll get exactly what you want. For this having right, you have to ensure that $fillable array consists all attributes' names passed to create method.
You should use Observer on model instead of listener (most likely creating method).
Personal preference using Eloquent is that you should use id for id (increment field) and forget custom settings between models because by default it is what relations expect and so on
public function secondModels()
{
return $this->hasMany(SecondModel::class);
}
is pretty much no brainer. But for having this working best way would be (also following recommendations of this guy) FirstModel::id, SecondModel::id, SecondModel::first_model_id; first_models, second_models as table names. Avoiding and/or skipping this kind of unification is lot of custom job afterward. I don't say it can't be done but it is lot of non-first-time-successful work done.
Also, if you want visitor to get something other than id field name, you can make computed field with accessor:
/**
* Get the user's counter.
*
* #return string
*/
public function getCounterAttribute(): string
{
return (string)$this->id;
}
Which you call then with $user->counter.
Also personal preference of mine is to have most possible descriptive variable names so uuid field of mine would be something like
$table->uuid('uuid4');
This is some good and easy to make practice of Eloquent use.
Saying all this let me just to say that create() and save() will return created object from database while insert() shall not do it.

Validation for three unique fields and soft deletes

Last year I made a laravel site with an events table where I needed three fields to be unique for any event (place, date and time). I wasn't able to set up a validation request to do this so I added an unique index for these three fields directly through phpmyadmin and catching the exception that could happen if a duplicated event was inserted.
So basically my store() method has a try/catch like this:
try {
$event = new Event;
$event->place = $request->input('place');
$event->date = $request->input('date');
$event->time = $request->input('time');
$event->save();
return view(...);
} catch (\Illuminate\Database\QueryException $e) {
// Exception if place-date-time is duplicated
if($e->getCode() === '23000') {
return view('event.create')
->withErrors("Selected date and time is not available");
}
}
Well, now I had to change the app so events could be soft deleted and I simply added the 'deleted_at' field to the unique index, thinking it would be so easy... This approach doesn't work anymore so I've been reading here and there about this problem and the only thing I get is I should do it through a validation request with unique, but honestly I just don't get the syntax for this validation rule with three fields that can't be equal while a fourth one, deleted_at, being null.
My app checks for the available places, dates and times and doesn't let the user choose any not available event but no matter how many times I've told them there's always someone who uses the browser back button and saves the event again :(
Any help will be much appreciated. Thank you!
This is not a good approach to solve the problem.
You can do follow things to solve this problem
Before insert into database get a specific row if exist from database
and store into a variable.
Then check the data is already stored into the database or not.
If data is already there create custom validation message using Message Bag Like below.
$ifExist = $event
->wherePlace(request->input('place'))
->whereDate(request->input('date'))
->whereTime(request->input('time'))
->exist();
if ($ifExist) return 'already exist';
It might help you.
#narayanshama91 have pointed the right way.
You said you would like to use the unique rule to validate the input but the problem is that last week there was a post in Laravel Blog warning users of a possible SQL Injection via the unique rule if the input is provided by the user.
I would highly advise you to NOT USE this rule in this case since you depend on users input.
The correct approach in your case would be #narayanshama91 answer.
$ifExist = $event
->wherePlace(request->input('place'))
->whereDate(request->input('date'))
->whereTime(request->input('time'))
->exist();
if ($ifExist) {
return 'already exist';
}

Cakephp 3.0 - Validating Input against data in another model

I'm a pretty new to Cakephp 3.0 and I'm quite stuck on data validation, or rather Application Rules.
I have a simple user registration form to create a new user in the table 'users'. In the user registration form, there is a field for "ticket_number" where the user has to enter a ticket number which must exist in the table tickets.ticket_number, and also tickets.registration_status must be false (that ticket had not had a user registered with it yet).
My tables look like this (simplified:)
users:
id | username | password
tickets:
id | ticket_number | user_id | registration_status
In my users model, I have defined (user can have many tickets):
$this->hasMany('Tickets', [
'foreignKey' => 'user_id'
]);
In my Tickets model, I have defined (a ticket belongs to a user):
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
In the users model, I added a rule to attempt to check whether the entered ticket_number exists in the tickets.ticket_number column:
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->isUnique(['username']));
$rules->add($rules->isUnique(['email']));
$rules->add($rules->existsIn
(['ticket_number'], 'Tickets.ticket_number'));
return $rules;
}
This gives me the error:
Error: Call to a member function primaryKey() on a non-object
I'm very stuck here. Can anyone advise on the right approach implementing this check? And then, how to also implement the check to see whether the associated registration_status is 0 (that ticket has not been registered yet).
Thanks in advance for any advice!
--D.
The existsIn rule will not solve your problem, as it will not take your additional condition into account (registration-status).
You will need to provide a custom rule to accomplish the problem you described.
Regarding your update: The rule will return true - so passing the rule, if your tables are not setup correctly or it cannot find the field you are referencing or your field is nullable, check the source here: http://api.cakephp.org/3.0/source-class-Cake.ORM.Rule.ExistsIn.html#74-91
OK, after considerable tinkering, I solved the first issue (On the user add form, the user must enter a ticket number which must exist in the table tickets.ticket_number.)
The only way I could get this to work using an existsIn rule in the UsersTable model, was to declare the ticket_number field as the primary key in the initialize method of the TicketsTable model:
$this->primaryKey('ticket_number');
Then the associations and existsIn rule in the UsersTable model worked. However, this isn't good as I don't want the primary key in tickets permanently set to ticket_number. It also doesn't solve my second problem (checking tickets.registration_status is false.)
So I took a different approach and instead used $this->loadModel('Tickets'); in the add method of my users controller. Then, I could easily perform the required checks:
//Look for this particular ticket
$ticketCount = $this->Tickets->find()->where(['Tickets.ticket_number' => $ticket_number])->count();
$this->set(compact('ticketCount'));
$this->set('_serialize', ['ticketCount']);
//If the ticket isn't found, then return with the Flash error
if (!$ticketCount > 0) {
$this->Flash->error(__('Sorry, but a Ticket with this number could not be found. Please, try again.'));
return;
} //otherwise move on...
I then similarly query the registration status of the ticket, and flash a different error back if it is already registered.
After the checks are passed, it goes on to the normal Add User checks and saving.
While this solution isn't as elegant as what I was hoping to do in the Users model with a custom association and rule, it works.
I'm also not convinced it can't be done in the model with a rule. But for now, this solution works.
Anyway thanks hmic for a response.
DBZ