I am learning symfony.
I try to save favorite when I click on a link and when I click again on the link, I wish I could remove the favorite in DB.
When clicking, I do have a new row in my database.
If I click again, it add a new one and don't erase the row.
This is what i have done in my controller:
public function addFavorite(EntityManagerInterface $manager, PostRepository $postRepository, Post $post)
{
$favorite = $postRepository->findOneBy(['content' => $post,'author' => $this->getUser()
]);
if (is_null($favorite)) {
$favorite = new Favorite();
$favorite
->setPost($post)
->setUser($this->getUser());
$manager->persist($favorite);
$manager->flush();
return $this->render('favorite/index.html.twig');
} else {
$manager->remove($favorite);
$manager->flush();
return $this->render('favorite/index.html.twig');
}
}
From what i understand, the problem is that $favorite is still NULL and i don't understand why...
If someone could help me, thanks !
I think it's because you don't request the good entity. I don't really know your entities, but I think this could be more logical :
public function addFavorite(EntityManagerInterface $manager, FavoriteRepository $favoriteRepository, Post $post)
{
$favorite = $favoriteRepository->findOneBy([
'post' => $post,
'user' => $this->getUser()
]);
if (!$favorite) {
$favorite = new Favorite();
$favorite
->setPost($post)
->setUser($this->getUser());
$manager->persist($favorite);
} else {
$manager->remove($favorite);
}
$manager->flush();
return $this->render('favorite/index.html.twig');
}
Related
How would you do a functional test (not unit tests) of a form binded to an entity ?
Context
Let's say you have an entity "Car", with a field "id" and another field "numberPlate", and a page to edit data about a car.
CarController.php :
//...
public function imsiDetailsChangeAction(Request $request)
{
$car_id = $request->get('car_id');
$car = $this->getDoctrine()->getRepository('ClnGsmBundle:Car')->Find($car_id);
if ($simCard != null)
{
$form = $this->createForm(new CarType()), $car);
if($request->isMethod('POST'))
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->flush();
return $this->redirect($this->generateUrl('car_view', array('car_id' => $car->getId())));
}
}
}
else
{
throw new NotFoundHttpException();
}
return $this->render('SiteBundle:Car:carEdit.html.twig', array('car' => $car, 'form' => $form->createView()));
}
//...
What I want
A test using phpUnit doing the following :
create a Car entity with the numberPlate "QWE-456"
load the page with the form
using the crawler, replace the numberPlate with "AZE-123" in the form, and submit the form
assert that my car entity's numberPlate now equals "AZE-123"
What I tried
(just in case: my own code is a bit different, here is what I would do with the car example)
CarControllerTest.php :
//...
public function SetUp()
{
//start kernel, stores entity manager in $this->em and client in $this->client
}
//...
public function testEditForm()
{
$car = new Car();
$car->setNumberPlate("QWE-456");
$this->entityManager->persist($simCard);
$this->em->flush();
$crawler = $this->client->request('GET', '/fr/Car/edit/'.$car->getId());
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$formNode = $crawler->filterXpath("//div[#id='main']//form");
$form = $formNode->form(array(
'car[plateNumber]'=>'AZE-123',
));
//var_dump($car->getPlateNumber());
$this->client->submit($form);
//var_dump($car->getPlateNumber());
$this->assertEquals('AZE-123',$car->getPlateNumber);
}
I expect this test to pass, and the second var_dump to print "AZE-123" instead of "QWE-456". But my entity isn't modified.
How should I do this ?
You should refresh the data reloading it from the database: the refresh method do it for you, so try this:
$this->client->submit($form);
$this->em->refresh($car);
$this->assertEquals('AZE-123',$car->getPlateNumber);
I suggest you to check before the HTTP Response in order to verify the correct interaction, as example:
$response = $this->client->getResponse();
$this->assertTrue($response->isRedirection());
Hope this help
I'm completely lost as to why this is happening, and it happens about 50% of the time.
I have a check to see if a user exists by email and last name, and if they do, run some code. If the user doesn't exist, then create the user, and then run some code.
I've done various testing with dummy data, and even if a user doesn't exist, it first creates them, but then runs the code in the "if" block.
Here's what I have.
if (User::existsByEmailAndLastName($params->email, $params->lastName)) {
var_dump('user already exists');
} else {
User::createNew($params);
var_dump("Creating a new user...");
}
And here are the respective methods:
public static function existsByEmailAndLastName($email, $lastName) {
return User::find()->where([
'email' => $email,
])->andWhere([
'last_name' => $lastName
])->one();
}
public static function createNew($params) {
$user = new User;
$user->first_name = $params->firstName;
$user->last_name = $params->lastName;
$user->email = $params->email;
$user->address = $params->address;
$user->address_2 = $params->address_2;
$user->city = $params->city;
$user->province = $params->province;
$user->country = $params->country;
$user->phone = $params->phone;
$user->postal_code = $params->postal_code;
return $user->insert();
}
I've tried flushing the cache. I've tried it with raw SQL queries using Yii::$app->db->createCommand(), but nothing seems to be working. I'm totally stumped.
Does anyone know why it would first create the user, and then do the check in the if statement?
Editing with controller code:
public function actionComplete()
{
if (Yii::$app->basket->isEmpty()) {
return $this->redirect('basket', 302);
}
$guest = Yii::$app->request->get('guest');
$params = new CompletePaymentForm;
$post = Yii::$app->request->post();
if ($this->userInfo || $guest) {
if ($params->load($post) && $params->validate()) {
if (!User::isEmailValid($params->email)) {
throw new UserException('Please provide a valid email.');
}
if (!User::existsByEmailAndLastName($params->email, $params->lastName)) {
User::createNew($params);
echo "creating new user";
} else {
echo "user already exists";
}
}
return $this->render('complete', [
'model' => $completeDonationForm
]);
}
return $this->render('complete-login-or-guest');
}
Here's the answer after multiple tries:
Passing an 'ajaxParam' parameters with the ActiveForm widget to define the name of the GET parameter that will be sent if the request is an ajax request. I named my parameter "ajax".
Here's what the beginning of the ActiveForm looks like:
$form = ActiveForm::begin([
'id' => 'complete-form',
'ajaxParam' => 'ajax'
])
And then I added this check in my controller:
if (Yii::$app->request->get('ajax') || Yii::$app->request->isAjax) {
return false;
}
It was an ajax issue, so thanks a bunch to Yupik for pointing me towards it (accepting his answer since it lead me here).
You can put validation like below in your model:
public function rules() { return [ [['email'], 'functionName'], [['lastname'], 'functionforlastName'], ];}
public function functionName($attribute, $params) {
$usercheck=User::find()->where(['email' => $email])->one();
if($usercheck)
{
$this->addError($attribute, 'Email already exists!');
}
}
and create/apply same function for lastname.
put in form fields email and lastname => ['enableAjaxValidation' => true]
In Create function in controller
use yii\web\Response;
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
else if ($model->load(Yii::$app->request->post()))
{
//place your code here
}
Add 'enableAjaxValidation' => false to your ActiveForm params in view. It happens because yii sends request to your action to validate this model, but it's not handled before your if statement.
I am building a bundle for private messages between my users.
Here is my inbox action from my controller. What it does is fetches the current user's messages, it passes the query to KNPpaginator to display a part of them. I also save how many results to be displayed on the page in the database. One form is a dropdown that sends how many results to display per page. The other form is made of checkboxes and a dropdown with actions. Based on which action was selected, I pass the id's of the messages(selected checkboxes id's) to another function called markAction(which is also a page that can mark one single message by going to the specific url)
public function inboxAction(Request $request)
{
$messages = $this->getDoctrine()->getRepository('PrivateMessageBundle:Message');
$mymsg = $messages->findMyMessages($this->getUser());
$message_settings = $this->getDoctrine()->getRepository('PrivateMessageBundle:MessageSettings');
$perpage = $message_settings->findOneBy(array('user' => $this->getUser()));
$pagerform = $this->createForm(new MessageSettingsType(), $perpage);
$pagerform->handleRequest($request);
if ($pagerform->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($perpage);
$em->flush();
}
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$mymsg,
$request->query->get('page', 1)/*page number*/,
$perpage ? $perpage->getResPerPage() : 10/*limit per page*/,
array('defaultSortFieldName' => 'a.sentAt', 'defaultSortDirection' => 'desc')
);
$form = $this
->createForm(
new ActionsType(),
$mymsg->execute()
);
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
$ids = array();
foreach ($data['checkboxes'] as $checkbox) {
$ids[] = $checkbox->getId();
}
$action = $data['inboxactions'];
$this->markAction($action, $ids);
return $this->redirectToRoute('private_message_inbox');
}
return $this->render(
'#PrivateMessage/inbox.html.twig',
array(
'messageList' => $pagination,
'form' => $form->createView(),
'pagerform' => $pagerform->createView(),
)
);
}
And the mark action user in my inbox controller. Based on one parameter, I apply the respective action to the second parameter, which is one message if the page was called through routing, and can be an array of messages if called through my inboxAction. I do a few consistency checks, and then mark my message.
public function markAction($action, $msgs)
{
if (!$msgs) {
$this->addFlash(
'error',
'Select at least one message!'
);
return;
} else {
if (!$action) {
$this->addFlash(
'error',
'Select one action to apply to your items!'
);
return;
} else {
$messages = $this->getDoctrine()->getRepository('PrivateMessageBundle:Message');
$em = $this->getDoctrine()->getManager();
$msg = $messages->findBy(array('receiver' => $this->getUser(), 'id' => $msgs));
$good = 0;
foreach ($msg as $isforme) {
$good++;
switch ($action) {
case 'spam': {
if ($isforme->getIsSpam() == false) {
$isforme->setIsSpam(true);
if (!$isforme->getSeenAt()) {
$isforme->setSeenAt(new \DateTime('now'));
}
$em->persist($isforme);
}
break;
}
case 'unspam': {
if ($isforme->getIsSpam() == true) {
$isforme->setIsSpam(false);
$em->persist($isforme);
}
break;
}
case 'viewed': {
if ($isforme->getSeenAt() == false) {
$isforme->setSeenAt(new \DateTime('now'));
$em->persist($isforme);
}
break;
}
case 'unviewed': {
if ($isforme->getSeenAt() != false) {
$isforme->setSeenAt(null);
$em->persist($isforme);
}
break;
}
default: {
$this->addFlash(
'error',
'There was an error!'
);
return;
}
}
$em->flush();
}
$this->addFlash(
'notice',
$good.' message'.($good == 1 ? '' : 's').' changed!'
);
}
}
if ($action == 'unspam') {
return $this->redirectToRoute('private_message_spam');
} else {
return $this->redirectToRoute('private_message_inbox');
}
}
Being kind of new to symfony, I'm not sure how good my markAction function is. I feel like it can be simplier, but I'm not sure how to make it.
Now, my actual question. How can I render other pages of my bundle, like Sent or Spam messages? The only lines from the inboxAction that I have to change are
$mymsg = $messages->findMyMessages($this->getUser());
to have it return spam or sent messages by the user, for instance.
and
return $this->render(
'#PrivateMessage/inbox.html.twig',...
so I actually return the respective page's view. I have already made the other pages and copied the code in the other actions, but I think I can make it so I write this code a single time, but don't know how.
Everything else is EXACTLY the same. How can I not copy and paste this code in all of the other actions and make it a bit more reusable?
You could strart to change your routing more dynamic:
# app/config/routing.yml
mailclient:
path: /mailclient/{page}
defaults: { _controller: AppBundle:Mailclient:index, page: "inbox" }
Resulting that this routes:
/mailclient
/mailclient/inbox
/mailclient/sent
/mailclient/trash
will all call the same action.
Now your method (Action) will get an extra parameter:
public function indexAction($page, Request $request)
{
// ...
}
Through this parameter you know what the user likes to see. Now you can start to write your code more dynamic. You can consider to add some private functions to your controller class that you can call from the indexAction or
you could simply create your own classes too.
I am currently working on a project using cakephp 3.
I have a form to add clients which works using this in my controller :
public function add(){
$clients = $this->Clients->newEntity();
if($this->request->is('post')){
$clients = $this->Clients->patchEntity($clients, $this->request->data);
if($this->Clients->save($clients)){
$this->Flash->success(__('Client has been created.'));
return $this->redirect(['controller'=>'Clients','action'=>'index']);
}
$this->Flash->error(__('Client hasnt been created.'));
}
$this->set('clients',$clients);
}
Then I want to have the possibility to modify one of my client.
I have a table of clients and when I click on of them, I have a modify button coming (jQuery).
Then I'm on my modify page. I did some test with the doc from cake but it seems I don't understand how it works and what tools should I use.
For the moment, I have this on my Controller:
public function modify($id = null){
if(empty($id)){
throw new NotFoundException;
}
$clients = $this->Clients->get($id);
/* there should be the modify code */
$this->set('clients', $clients);
}
I don't really know what to use as I said... Any help pls?
The code for editing a record is quite straight forward:
public function modify($id = null){
if(empty($id)){
throw new NotFoundException;
}
$client = $this->Clients->get($id);
if ($this->request->is(['post', 'put']) {
$client = $this->Clients->patchEntity($client, $this->request->data);
if ($this->Clients->save($client)) {
return $this->redirect($someURL);
}
}
$this->set('client', $client);
}
I am trying to build an upvote system in cakephp but I am having some trouble and am ending up with unidentified index errors and array to string conversion.
This is my function in my PostsController:
public function like ($id=null, $like=NULL) {
if(!$id) {throw new NotFoundException(__('Invalid post'));
}
$post = $this -> Post-> findById($id);
$like = $this->Like->find('all', array(
'conditions' => array('username' =>
array($this->Auth->user('username')))
));
if(!$post) {throw new NotFoundException(__('Invalid post'));
}
$this -> set('post',$post);
$this -> set('like', $like);
if ($like['Like']['username'] == $post['Post']['username'] && $like['Like']['article_id'] == $post['Post']['id']){
$this->redirect(array('action'=>'index'));
}
else{
$this->Like->saveField('username', $this->Auth->user('username'));
$this->Like->saveField('article_id', $post);
$this->redirect(array('action'=>'index'));
}
}
At the top of my controller I do var $uses = array('Post','Like'); So my PostsController knows to use the Like model too. Now I know what problem is, I just don't know how to fix it. When I set the fields, username gets set in the DB, but $post returns an array of all posts. What I want to happen is for it to only return the current post I am on. This is what I am doing in my view:
<?php echo $this->Html->link(
'Like',
array('action'=>'Like',$post['Post']['id']));
?>
And this is the action that goes with that view:
public function view ($id=null) {
if(!$id) {throw new NotFoundException(__('Invalid post'));
}
$post = $this -> Post-> findById($id);
if(!$post) {
throw new NotFoundException(__('Invalid post'));
}
$this -> set('post',$post);
}
How can I get my link function to only return the current post I want to like and not an array of all the posts?
EDIT - Forgot to mention I'm getting an Undefined index: Like error on the 13th line of my posts controller.
You have
$like = $this->Like->find('all', array(
'conditions' => array('username' =>
array($this->Auth->user('username')))
));
This will give an array with more items, so you cannot use $like['Like']. This is why you are getting the warning.
You could use $like[0]['Like'].
If you need to go through each of the likes, you can do
foreach ($like as $currentLike) {
if ($currentLike['Like']['username'] == $post['Post']['username'] ....
}
Please include more details about why you are making these comparisons and redirects, and maybe more of the code can be refactored.