How to properly use and override beforeSave method in Yii2 - overriding

I have override the existing \yii\db\ActiveRecord with my own class. The method I've override is beforeSave(). I've read the documentation about the usage. But I found it's called twice when checking the record whether it is a new record or not.
This is my code:
class ActiveRecord extends \yii\db\ActiveRecord{
public $count = 0;
public function beforeSave($insert) {
print($this->count++); //i try to investigate it deeper using this "count" property
if(parent::beforeSave($insert)){
if($this->isNewRecord){
print("123"); //this printed out
if($this->hasAttribute('user_create')){
$this->user_create = \Yii::$app->user->identity->id;
}
if($this->hasAttribute('time_create')){
$this->time_create = new \yii\db\Expression('now()');
}
}
else{
print("456"); //and this is also
if($this->hasAttribute('user_upd')){
$this->user_upd = \Yii::$app->user->identity->id;
}
if($this->hasAttribute('time_upd')){
$this->time_upd = new \yii\db\Expression('now()');
}
}
return true;
}else{
return false;
}
}
}
and the output of the code when I do save a new record is as follow
01231456

I made a mistaken in the Controller Side, this is my Code
public function actionCreate() {
$model = new Grup();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
if ($model->save()) {
echo Json::encode(array('status' => 1));
} else {
echo Json::encode(array('status' => 0, 'message' => \kartik\widgets\ActiveForm::validate($model)));
}
} else {
return $this->renderAjax('create', [
'model' => $model,
]);
}
}
I shouldn't use $model->save() twice.

Related

How can I easily check whether the user submitting a query belongs to them or not in .net core?

Authorization Set
services.AddAuthorization(options =>
{
options.AddPolicy("MustNutritionist", policy =>
policy.RequireClaim("nutritionistId"));
});
Controller
NutritionistUpdateModel have id field.
[Authorize(Policy = "MustNutritionist")]
public BaseResponseModel PostEdit([FromForm] NutritionistUpdateModel nutritionistUpdateModel)
{
try
{
var result = nutritionistService.EditNutritionist(nutritionistUpdateModel);
if (result)
{
return new SuccessResponseModel<bool>(result);
}
else
{
return new BaseResponseModel(ReadOnlyValues.NutritionistNotFound);
}
}
catch (Exception ex)
{
return new BaseResponseModel(ex.Message);
}
}
Token Generation Claim
claims.Add(new Claim("nutritionistId", nutritionistId.ToString()));
Problem
I want to check equation of NutritionistUpdateModel.Id and Claims.nutritionistId. I can check with below code.But i must write lots of if else statement.Is there any easy way ?
private bool ChechNutritionistAuthorize(int nutritionistId)
{
var currentUser = HttpContext.User;
var nutritionistIdClaim=Int32.Parse(currentUser.Claims.FirstOrDefault(c => c.Type == "NutritionistId").Value);
if (nutritionistIdClaim == nutritionistId)
{
return true;
}
else
{
return false;
}
}
Using extension method like this
public static class IdentityExtensions
{
public static bool ValidateNutritionistId(this ClaimsPrincipal principal, int nutritionistId)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
int.TryParse(principal.Claims.FirstOrDefault(c => c.Type == "NutritionistId").Value, out int nutritionistIdClaim);
return nutritionistIdClaim == nutritionistId;
}
}
and you can use like this
HttpContext.User.ValidateNutritionistId(your id here )
and you also need to add using statement and reuse same method in all of your Controllers

How to control models included in NSwag document

I have 2 areas in my ASP.NET Core 3.1 API and models in each of them. I'm trying to produce 2 swagger documents, each for its area.
Operations are filtered based on IOperationProcessor, and all operations are correct. However, each document includes all documents from both areas.
What is the way to control which models included in which document?
This is what I have at the moment:
services.AddOpenApiDocument(swagger =>
{
swagger.DocumentName = "admin";
swagger.OperationProcessors.Add(new Swagger.AdminOperationProcessor());
swagger.SchemaNameGenerator = new Swagger.SchemaNameGenerator();
});
services.AddOpenApiDocument(openApi =>
{
openApi.DocumentName = "public";
openApi.OperationProcessors.Add(new Swagger.PublicOperationProcessor());
openApi.SchemaNameGenerator = new Swagger.SchemaNameGenerator();
openApi.PostProcess = (x) =>
{
};
});
After looking for ages, what I had to do is a bit of a botched solution. Using ISchemaNameGenerator replace not desired names into something else (I replace "." to "_"). Then using PostProcess remove them from generated schema. ExcludedTypeNames doesn't work for some reason.
var excludedList = new List<string>();
// this is called first
settings.SchemaNameGenerator = new SchemaNameGenerator(x =>
{
if (!typeNamespaces.Any(n => x.FullName.StartsWith(n)))
{
excludedList.Add(x.FullName);
}
return !typeNamespaces.Any(n => x.FullName.StartsWith(n));
});
// Post process relies on excluded list, otherwise it won't work
settings.PostProcess = (doc) =>
{
foreach (var exItem in excludedList)
{
var formatted = exItem.Replace(".", "_");
if (doc.Definitions.ContainsKey(formatted))
{
doc.Definitions.Remove(formatted);
}
}
};
The SchemaNameGenerator is:
public class SchemaNameGenerator : ISchemaNameGenerator
{
private Func<Type, bool> excludePredicate;
public SchemaNameGenerator(Func<Type, bool> excludePredicate = null)
{
this.excludePredicate = excludePredicate;
}
public string Generate(Type type)
{
var displayAttr = type
.GetCustomAttributes(typeof(DisplayNameAttribute), true)
.Cast<DisplayNameAttribute>()
.FirstOrDefault();
if (excludePredicate != null)
{
if (excludePredicate(type))
{
return type.FullName.Replace(".", "_");
}
}
return displayAttr?.DisplayName ?? type.Name;
}
}
If anybody has a better idea, please let me know.

How to test service function without connecting on database

I'm turning again on you guys, because I spend my fair share of hours on this "task" and I still can't figure out how to test my service method, without my function connection on database (I have to mock repository functions)
This is my service function
public function getInfo($history, $name)
{
$requestRepository = $this->em->getRepository(Request::class);
if ($history) {
$requests = [];
foreach ($requestRepository->getRequestsByName($name) as $request) {
$requests[] = $requestRepository->transform($request);
}
return $requests;
} else {
$request = $requestRepository->getCompletedRequestByName($name);
if (!is_null($request)) {
return $requestRepository->transform($request);
} else {
return null;
}
}
}
And this is my test
public function testGetInfo()
{
/* This returns errors, because it tries to connect to DATABASE, but I don't wan't that, that's why I figure out I need to mock this
$requestManager = new RequestManager($this->entityManager);
$test = $requestManager->getInfo('histroy', 'antrax.com');
*/
$requestManager = $this->getMockBuilder(RequestManager::class)->disableOriginalConstructor()->setMethods(['getInfo'])
->getMock();
// And rest of this are just my FAILED attempts to figure out, how to test my methods
$queryBuilder = $this->getMockBuilder(RequestRepository::class)->disableOriginalConstructor()
->setMethods(['getInfo'])->getMock();
$test = $queryBuilder->method('getInfo')->willReturnSelf();
$queryBuilder->method('getInfo')->willReturnCallback(function ($field, $value) use ($queryBuilder, $test){
if ($field == 'newStatus') {
$this->assertSame('EXPIRED', $value);
}
return $queryBuilder;
});
}
Can some one please help me how to write a test for my method getInfo so it will have 100% cover. If you need any additional informations, please let me know and I will provide. Thank you!
This is an answer to my problem
public function testGetInfo()
{
$mockEntity = $this->mockEntityManager;
$name = 'antrax.com';
$requestMock = new RequestEntity();
$transformedRequest = [
'id' => 1
];
$requestRepo = $this->getMockBuilder(RequestRepository::class)->disableOriginalConstructor()
->setMethods(['getRequestsByName', 'transform', 'getCompletedRequestByName'])->getMock();
$requestRepo->method('getRequestsByName')->willReturnCallback(function ($passedName) use ($name, $requestMock) {
$this->assertSame($name, $passedName);
return [$requestMock, $requestMock];
});
$requestRepo->method('transform')->willReturnCallback(function ($request) use ($requestMock, $transformedRequest) {
$this->assertSame($requestMock, $request);
return $transformedRequest;
});
$i = 0;
$requestRepo->method('getCompletedRequestByName')->willReturnCallback(function ($passedName) use ($name, $requestMock, &$i) {
$this->assertSame($name, $passedName);
if ($i == 0) {
$i+=1;
return null;
} else {
return $requestMock;
}
});
$mockEntity->method('getRepository')->willReturnCallback(function ($requestClass) use ($requestRepo) {
$this->assertSame(RequestEntity::class, $requestClass);
return $requestRepo;
});
$requestManager = new RequestManager($mockEntity);
$this->assertSame([$transformedRequest, $transformedRequest], $requestManager->getInfo(true, $name));
$this->assertNull($requestManager->getInfo(false, $name));
$this->assertSame($transformedRequest, $requestManager->getInfo(false, $name));
}

Access session variable from different controller in symfony

i want to create an array as a session table to put it empty in the beggining for my cart shop to display nothing as an empty cart and when i press AddtoCart i want get that array and do an array_push with the new items but i didn't know how to do it
This is the first controller when i create the array empty
public function FrontAction()
{
$pro=new Produit();
$pro->setNom('');
$pro->setQuantite(0);
$pro->setPrix(0);
$em = $this->getDoctrine()->getManager();
$sess=new Session();
$sess->setName('PANIER');
$sess=array('');
array_push($sess,$pro->getNom(),$pro->getQuantite(),$pro->getPrix());
$paniers = $em->getRepository(Panier::class)->findByUserId(1);
$produits = $this->getDoctrine()->getRepository(Produit::class)->findAll();
$boutiques = $this->getDoctrine()->getRepository(Boutique::class)->GetBestShopsDQL();
if ($paniers != null)
{
$prixTotal = 0;
foreach ($paniers as $panier) {
$prixTotal += $panier->getPrixTotal();
}
$resultCount = count($paniers);
return $this->render('BoutiqueBundle:FrontViews:ListBoutique.html.twig', array('produits' => $produits, 'boutiques' => $boutiques,'paniers' => $paniers, 'prixTotal' => $prixTotal,'resultCount' => $resultCount));
}
return $this->render('BoutiqueBundle:FrontViews:ListBoutique.html.twig', array('produits' => $produits, 'boutiques' => $boutiques,'sess'=>$sess));
}
and this is the second controller where i want to fill that empty array with new items
public function ajouterauPanierAction($id)
{
$ses=new Session();
$ses->getName('PANIER');
$test=array('');
$test=$ses;
// $user_id = $this->getUser()->getId(); //à modifier!!!!!
$em = $this->getDoctrine()->getManager();
$produit = $em->getRepository(Produit::class)->find($id);
$test = $em->getRepository(Panier::class)->findExistant($id, 1);
// $session->replace(array_push($produit,));
if(($produit != null)&&(empty($test)))
{
array_push($test,$produit->getNom(),$produit->getQuantite(),$produit->getPrix());
/* $panier = new Panier();
$panier->setProduitId($produit->getId());
$panier->setUserId(1); //à changer avec le fos
$panier->setDate(new \DateTime("now"));
$panier->setPrixTotal($produit->getPrix());
$em->persist($panier);
*/ $em->flush();
$msg = "success";
// return $this->redirectToRoute('Dashboard');
}
else
{
//return $this->render('BoutiqueBundle:FrontViews:404.html.twig');
$msg = "failure";
}
return new JsonResponse(array('msg' => $msg));
}
i didn't know how to do it correctly or if my idea is wrong so hope u guys got what i need to do
Here is how I am doing it in Symfony 4, (I think this part is unchanged). First I have declared my session keys as class constants on the entities to avoid collisions.
class Appointment
{
const SESSION_KEY = 'confirmed_app_entity_appointment';
...
}
Then in the controller use the SessionInterface and it will get autowired into your controller method with a typehinted parameter (SessionInterface $session). Symfony will handle starting the session, just set, get, and/or remove the key as needed.
use Symfony\Component\HttpFoundation\Session\SessionInterface;
...
public function confirmAppointment($first_name, $last_name, $time, SessionInterface $session)
{
...
$session->set(Appointment::SESSION_KEY, $appointment);
$session->set(StaffMember::SESSION_KEY_SELECTED_STAFF, $member);
...
if ($this->isConflict($appointment)) {
$session->remove(Appointment::SESSION_KEY);
$session->remove(StaffMember::SESSION_KEY_AUTH);
$this->addFlash('error', 'Failed to create Appointment, conflict found');
return $this->redirectToRoute('customer');
}
...
}
public function saveAppointment(SessionInterface $session)
{
if (!empty($session->get(Appointment::SESSION_KEY))) {
$appointment = $session->get(Appointment::SESSION_KEY);
}
...
}

symfony - saving blob data image sizes

I have a form, PhotoForm, which has an emebedded BlobDataForm.
I can save the blbo data fine, my problem comes, with the blob_data table.
I have 2 fields, image_width and image_height.
I'd like to save these details as well, when the blob is saved.
I have overridden doSave();
protected function doSave($con = null)
{
if (null === $con)
{
$con = $this->getConnection();
}
$this->updateObject();
$blobData = new BlobData();
$this->saveEmbeddedForms($con);
$this->getObject()->setBlobData($this->getEmbeddedForm('blob_data')->getObject());
$this->getObject()->save($con);
}
Would I need to override saveEmbeddedForms() as well?
Thanks
EDIT:
Ok, so it seems i need to override:
processValues()
I'm just having trouble getting the images width and height attributes.
Does anyone know how I'd do that?
Thanks
If you can get this 2 informations from your blob_data field, you can override the preSave method of your BlobData class which is called just before saving object :
public function preSave($event)
{
//get the information from the blob_data
$this->image_width = ... ;
$this->image_height = ... ;
}
Right, so after all that, I had to override saveEmbeddedForms:
public function saveEmbeddedForms($con = null, $forms = null)
{
if (null === $con)
{
$con = $this->getConnection();
}
if (null === $forms)
{
$photos = $this->getValue('blob_data');
$forms = $this->embeddedForms;
foreach ($this->embeddedForms['blob_data'] as $name => $form)
{
if (!isset($photos[$name]))
{
unset($forms['blob_data'][$name]);
}
}
}
foreach ($forms as $form)
{
if ($form instanceof sfFormObject)
{
$form->saveEmbeddedForms($con);
$blobData = $form->getObject()->getBlobData();
$imageStream = stream_get_contents($blobData);
$image = imagecreatefromstring($imageStream);
$form->getObject()->setImageWidth(imagesx($image));
$form->getObject()->setImageHeight(imagesy($image));
$form->getObject()->setFileExtension('jpg');
//return parent::preSave($con);
$form->getObject()->save($con);
}
else
{
$this->saveEmbeddedForms($con, $form->getEmbeddedForms());
}
}
}
This seemed to work for me
Thanks

Resources