Yii2 before Validate throw PHP Warning - yii2

I used beforeValidate($insert) function and it thrown a PHP Warning when I access my post listing page:
http://localhost/yiiapp/backend/web/index.php?r=post/index
PHP Warning – yii\base\ErrorException
Missing argument 1 for app\models\Post::beforeValidate(), called in /var/www/html/yiiapp/vendor/yiisoft/yii2/base/Model.php on line 341 and defined
but when I access my create page, this Exception gone away:
http://localhost/yiiapp/backend/web/index.php?r=post/create
Actually I want to assign value one of my attribute user_id before validation in Post Model.
Here is Post Model:
class Post extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'post';
}
public function beforeValidate($insert)
{
if (parent::beforeValidate($insert)) {
$this->user_id = Yii::$app->user->id;
return true;
}
return false;
}
---
}
Why this Exception?
How I can solve this issue?

According to doc http://www.yiiframework.com/doc-2.0/yii-base-model.html#beforeValidate()-detail method beforeValidate has no attributes. Use it this way:
public function beforeValidate()
{
if (parent::beforeValidate()) {
return true;
}
return false;
}

Related

hasOne with null-able in laravel not working

I have a customer table which has a field called 'policy_id', where policy_id points to policy table. It is a null-able field, ie. Some customers may not have a policy.
I have a relationship code like this in Customer.php
public function policy() {
return $this->hasOne('App\Models\Policy', "id", "policy_id");
}
But when I issue a search request I am getting error like this:
Illuminate\Database\Eloquent\ModelNotFoundException: No query results for model [App\Models\Policy]
If I modify the function like this:
public function policy() {
if ($this->getAttribute('policy_id')) {
return $this->hasOne('App\Models\Policy', "id", "policy_id");
} else {
return null
}
}
But I am getting an error like this:
Call to a member function getRelationExistenceQuery() on null
Here is my search code:
$c = new Customer();
return Customer::doesntHave('policy')->orWhere(function (Builder $query) use ($req) {
$query->orWhereHas('policy', function (Builder $query) use ($req) {
$p = new Policy();
$query->where($req->only($p->getFillable()))
->orWhereBetween("policy_period_from", [$req->policy_period_start_from, $req->policy_period_start_to])
->orWhereBetween("policy_period_to", [$req->policy_period_end_from, $req->policy_period_end_to])
->orWhereBetween("payment_date", [$req->payment_date_from, $req->payment_date_to]);
});
})->where($req->only($c->getFillable()))->get();
Am I missing something or are there any other ways to do this?
PS: While debugging the above search code is returning successfully, but the exception happening from somewhere inside Laravel after the prepareResponse call.
Thanks in advance.
return $this->hasOne('App\ModelName', 'foreign_key', 'local_key');
Change the order, put the foreign_key policy_id in front of id
In your Customer Model, you need to use belongsTo method:
public function policy() {
return $this->belongsTo('App\Models\Policy', "policy_id", "id");
}
And In your Policy Model, use hasOne:
public function customer() {
return $this->hasOne('App\Models\Customer', "policy_id", "id");
}
First of all, you placed the wrong params.
$this->belongsTo('App\Models\Policy', "FK", "PK");
public function policy() {
return $this->belongsTo('App\Models\Policy','policy_id', 'id');
}
And for null value of policy_id you can use withDefault();
public function policy() {
return $this->belongsTo('App\Models\Policy','policy_id', 'id')->withDefault([
'name' => 'test'
]);;
}
there's a number of problems there but can you perhaps specify the namespace and the class of both your models - Customer and Policy.
By default, the models you create with php artisan make:model will use the \App namespace e.g. \App\Customer and \App\Policy.
Just double check that.
Also, with regards to the relationship, if the Laravel conventions have been followed you could just:
In the Customer model
public function policy() {
return $this->belongsTo(Policy::class);
}
In the Policy model
public function customer() {
return $this->hasOne(Customer::class);
}
of if a multiple customers can be under one policy
public function customers() {
return $this->hasMany(Customer::class);
}
Good luck

Adding withCount to collection json

So i've got two models, Client and Project, and Client has a hasMany relationship with projects. I'm just trying to add the project count for the client into the JSON response but I can't get it to work.
My controller just returns all the projects;
public function index(Client $client)
{
return $client->all();
}
And my model contains the below;
protected $appends = ['client_projects_count'];
/**
* Create a relationship between this client and it's projects
*/
public function clientProjects() {
return $this->hasMany('App\Project');
}
public function getClientProjectsCountAttribute()
{
return $this->withCount('clientProjects');
}
This just adds client_projects_count to the response but it's an empty array. I'm close, if I dd($this->withCount('clientProjects')->get()) I can see the client_projects_count with the correct count, but if I remove the dd and keep the ->get() then I get an internal server error.
Also, it is possible to only load this for the index() method rather than every one?
From the Documentation
$clients = Client::withCount('projects')->get();
foreach ($clients as $client) {
echo $client->projects_count;
}
So I managed to resolve it myself, although I'm sure their must be a nicer way.
Client.php
protected $appends = ['client_projects_count'];
/**
* Create a relationship between this client and it's projects
*/
public function clientProjects() {
return $this->hasMany('App\Project');
}
public function clientProjectsCount()
{
return $this->clientProjects()->selectRaw('client_id, count(*) as aggregate')->groupBy('client_id')->get();
}
public function getClientProjectsCountAttribute()
{
return isset($this->clientProjectsCount()[0]) ? $this->clientProjectsCount()[0]->aggregate : 0;
}

return afterSave in model yii2

If there is one controller yii2 with one model in which, for example, the outline only:
class MyController class extends Controller {
public function actionCreate() {
$valid = $model->validate();
}
}
Need the return from aftersave, which is a message indicating if the email was able to be sent or not, and all the attributes were inserted.
afterSave in the model:
public function afterSave($insert, $changedAttributes) {
if(!$this->isNewRecord) {
try {
Yii::$app->mailer->compose()
// email composition here
->send();
return 'mail sent';
catch (\Swift_SwiftException $exception) {
return 'email undeliverable'. $exception->getMessage();
}
}
}
Can the return from afterSave be accessed by the controller?
No, you will not be able to access any of those messages, because:
In the controller you are calling $model->validate() and afterSave() will never be called. You would need to call $model->save().
afterSave() is not supposed to return anything, so the value you are returning is lost. See the function definition in the official documentation here
No, it's not gonna trigger the afterSave event with your code. \yii\db\BaseActiveRecord::afterSave is being triggered in 2 places
\yii\db\BaseActiveRecord::updateInternal
\yii\db\ActiveRecord::insertInternal
and if you take a look at \yii\base\Model::validate you'll see that afterSave isn't calling in here.

LINQ to SQL validate all fields, not just stop at first failed field

I just started using LINQ to SQL classes, and really like how this helps me write readable code.
In the documentation, typical examples state that to do custom validation, you create a partial class as so::
partial class Customer
{
partial void OnCustomerIDChanging(string value)
{
if (value=="BADVALUE") throw new NotImplementedException("CustomerID Invalid");
}
}
And similarly for other fields...
And then in the codebehind, i put something like this to display the error message and keep the user on same page so to correct the mistake.
public void CustomerListView_OnItemInserted(object sender, ListViewInsertedEventArgs e)
{
string errorString = "";
if (e.Exception != null)
{
e.KeepInInsertMode = true;
errorString += e.Exception.Message;
e.ExceptionHandled = true;
}
else errorString += "Successfully inserted Customer Data" + "\n";
errorMessage.Text = errorString;
}
Okay, that's easy, but then it stops validating the rest of the fields as soon as the first Exception is thrown!! Mean if the user made mode than one mistake, she/he/it will only be notified of the first error.
Is there another way to check all the input and show the errors in each ?
Any suggestions appreciated, thanks.
This looks like a job for the Enterprise Library Validation Application Block (VAB). VAB has been designed to return all errors. Besides this, it doesn't thrown an exception, so you can simply ask it to validate the type for you.
When you decide to use the VAB, I advise you to -not- use the OnXXXChanging and OnValidate methods of LINQ to SQL. It's best to override the SubmitChange(ConflictMode) method on the DataContext class to call into VAB's validation API. This keeps your validation logic out of your business entities, which keeps your entities clean.
Look at the following example:
public partial class NorthwindDataContext
{
public ValidationResult[] Validate()
{
return invalidResults = (
from entity in this.GetChangedEntities()
let type = entity.GetType()
let validator = ValidationFactory.CreateValidator(type)
let results = validator.Validate(entity)
where !results.IsValid
from result in results
select result).ToArray();
}
public override void SubmitChanges(ConflictMode failureMode)
{
ValidationResult[] this.Validate();
if (invalidResults.Length > 0)
{
// You should define this exception type
throw new ValidationException(invalidResults);
}
base.SubmitChanges(failureMode);
}
private IEnumerable<object> GetChangedEntities()
{
ChangeSet changes = this.GetChangeSet();
return changes.Inserts.Concat(changes.Updates);
}
}
[Serializable]
public class ValidationException : Exception
{
public ValidationException(IEnumerable<ValidationResult> results)
: base("There are validation errors.")
{
this.Results = new ReadOnlyCollection<ValidationResult>(
results.ToArray());
}
public ReadOnlyCollection<ValidationResult> Results
{
get; private set;
}
}
Calling the Validate() method will return a collection of all errors, but rather than calling Validate(), I'd simply call SubmitChanges() when you're ready to persist. SubmitChanges() will now check for errors and throw an exception when one of the entities is invalid. Because the list of errors is sent to the ValidationException, you can iterate over the errors higher up the call stack, and present them to the user, as follows:
try
{
db.SubmitChanges();
}
catch (ValidationException vex)
{
ShowErrors(vex.ValidationErrors);
}
private static void ShowErrors(IEnumerable<ValidationResult> errors)
{
foreach(var error in errors)
{
Console.WriteLine("{0}: {1}", error.Key, error.message);
}
}
When you use this approach you make sure that your entities are always validated before saving them to the database
Here is a good article that explains how to integrate VAB with LINQ to SQL. You should definitely read it if you want to use VAB with LINQ to SQL.
Not with LINQ. Presumably you would validate the input before giving it to LINQ.
What you're seeing is natural behaviour with exceptions.
I figured it out. Instead of throwing an exception at first failed validation, i store an error message in a class with static variable. to do this, i extend the DataContext class like this::
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for SalesClassesDataContext
/// </summary>
public partial class SalesClassesDataContext
{
public class ErrorBox
{
private static List<string> Messages = new List<string>();
public void addMessage(string message)
{
Messages.Add(message);
}
public List<string> getMessages()
{
return Messages;
}
}
}
in the classes corresponding to each table, i would inherit the newly defined class like this::
public partial class Customer : SalesClassesDataContext.ErrorBox
only in the function OnValidate i would throw an exception in case the number of errors is not 0. Hence not attempting to insert, and keeping the user on same input page, without loosing the data they entered.

Howto allow any data type to be returned by a function in actionscript 3?

I have a static Settings class where my application can retrieve settings from. The problem is that some of these settings are strings, while others are ints or numbers. Example:
package
{
public final class Settings
{
public static function retrieve(msg:String)
{
switch (msg)
{
case "register_link":
return "http://test.com/client/register.php";
break;
case "time_limit":
return 50;
break;
}
}
}
}
Now, in the first case it should send a string and in the second a uint. However, how do I set this in the function declarement? Instead of eg. function retrieve(msg:String):String or ...:uint? If I don't set any data type, I get a warning.
HanClinto has answered your question, but I would like to also just make a note of another possible solution that keeps the return types, typed. I also find it to be a cleaner solution.
Rather than a static retrieve function, you could just use static consts, such as:
package
{
public final class Settings
{
public static const REGISTER_LINK:String = "my link";
public static const TIME_LIMIT:uint= 50;
}
}
And so forth. It's personal preference, but I thought I would throw it out there.
Use *
public static function retrieve(msg:String):*
{
if (msg == "age") {
return 23;
} else {
return "hi!";
}
}