Update MySQL database Symfony2 - mysql

I have a MySQL database and frontend DataTables view and work with Symfony.
I have 2 buttons on my tables. Delete and edit.
If I click on delete, it deletes the row from the DataTables and from MySqL. Works with this code:
/**
* #Route("/delete/{id}", name="deletepage")
* #Template()
*/
public function delete($id)
{
$em = $this->getDoctrine()->getManager();
$events = $em -> getRepository('AppBundle:eqAli')->find($id);
$em -> remove($events);
$em -> flush();
return $this->redirectToroute('homepage');
}
I have an edit button, which redirects to an edit page, where the data from that row is auto-filled.
What I am trying to achieve is that once there is made a change by a user on that page and the user clicks on save, that the new information will be updated into MySQL. So no delete, but update.
I am trying to achieve that with this code:
/**
* #Route("/edit/update/{id}", name="updatepage")
* #Template()
*/
public function update($id)
{
$em = $this->getDoctrine()->getManager();
$events = $em -> getRepository('AppBundle:eqAli')->find($id);
$em -> persist($events);
$em -> flush();
return $this->redirectToroute('homepage');
}
But that does nothing. It does get the right ID, I can click, just no update into MySQL. The path etc is right because when I change persist into delete, the button works.
Anyone a solution?

Basically, you are fetching single eqAli entity, ordering Entity Manager to manage it and flush all changes to your entities into database.
The problem is that you are not making actual changes to your entity before saving.

Ehhh ...
All should be in editAction (no redirect to update route necessary) , below you have working example, try to implement it on your case (it's a little bit more advanced , but you should catch idea ) .
```
/**
* #ParamConverter(name="reklama", class="SomeBundle:Reklamy")
* #Template()
* #param Request $request
* #param Reklamy $reklama
*/
public function editAction(Request $request,Reklamy $reklama){
$em = $this->getDoctrine()->getManager();
$form=$this->createForm(new ReklamyType(),$reklama);
if($request->getMethod()=="POST"){
$form->handleRequest($request);
if($form->isValid()){
$em->flush();
return $this->redirectToRoute('someroute')
}
}
return array('form'=>$form->createView());
}

You need to do something with the $events entity that you found, like so:
$events = $em -> getRepository('AppBundle:eqAli')->find($id);
...
$events->modifyDate( new DateTime() );
...
$em -> persist($events);
$em -> flush();
Where for example modifyDate is a method that is part of the eqAli Entity. I show ... before and after to indicate you might need to do other changes to your entity. In your original code, you are not changing anything at all. That's why you don't see any differences in the database.

Related

Symfony4.1 Doctrine ManyToMany Reduce No of Queries

I'm working on a project. Entity are Blog,Category,Tags. Blog and Tags are in ManyToMany Relation. My repository query to fetch data by Tags filter is.
CODE1:
/**
* #return BlogPost[]
*/
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('p')
// ->select('p','t')
->innerJoin('p.blogTags', 't')
->where('t.slug = :val')
->setParameter('val', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
This code works fine. All the tags(No of tags in a post)are displayed correctly. But the No of DB Query is 14. Then When I uncomment select as this,
CODE2:
/**
* #return BlogPost[]
*/
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('p')
->select('p','t')
->innerJoin('p.blogTags', 't')
->where('t.slug = :val')
->setParameter('val', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
No of Query is 9. But The Tags per Post is only one(Not displaying all the tags of a single post).
To be clear info:
It displays entire list of BlogPost.
But not all Tags of a Post.
Only one Tag per Post is shown.
Question: Is code1 is correct (No of DB Query = 14) or Do I have to tweak little bit to reduce no of DB Hits. Please guide me on this.
This is the expected behaviour in both cases.
Case 1) You just select the BlogPost entities. So you tell doctrine to fetch all BlogPosts that have the BlogTag that has slug = value.
The SQL query produced returns only column values from the blog_post table and so only hydrates the BlogPost entities returned, it does not hydrate the collection of BlogTags inside each BlogPost.
When you try to access the tags of a BlogPost a new query is generated to get and hydrate its collection.
That is the reason you get more queries in this case.
Case 2) You select also the filtered BlogTag entities, and doctrine hydrates(puts) only this filtered BlogTag to each BlogPost `s collection.
When you try to access the BlogTags of a BlogPost, you get the filtered one that meets the condition in the querybuilder.
To force doctrine to "reload" the data from the database, you should refresh the blogPost entity:
$em->refresh($blogPost);
and also include refrech option on cascade operations of the relation definition:
#OneToMany(targetEntity="BlogTag", mappedBy="post", cascade={"refresh"})
References:
what cascade refresh means in doctrine 2
refresh objects: different question but same solution
Thanks #Jannes Botis for refresh. But in my case the code itself is wrong. There need a slight change in it.
BlogTags.php
/**
* #ORM\ManyToMany(targetEntity="BlogPost", mappedBy="blogTags")
*/
private $blogPosts;
BlogPost.php
/**
* #var Collection|BlogTags[]
*
* #ORM\ManyToMany(targetEntity="BlogTags", inversedBy="blogPosts", cascade={"refresh"})
* #ORM\JoinTable(
* name="fz__blog_n_tag",
* joinColumns={
* #ORM\JoinColumn(name="blog_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id")
* }
* )
* #ORM\OrderBy({"name": "ASC"})
*/
private $blogTags;
This created the join_table. Allready I have a join_table. Although This code is for reference to someone.
Controller.php
// This is my old Code
$bp = $em->getRepository('App:BlogPost')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
// This is my New Code
$bp = $em->getRepository('App:BlogTags')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
Repository.php
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('t')
->select('t','p','tx')
->innerJoin('t.blogPosts', 'p')
->innerJoin('p.blogTags', 'tx')
->where('p.isActive = :val1')
->andWhere('t.slug = :val2')
->setParameter('val1', true)
->setParameter('val2', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
I not changed my old twig file completely. As it throws error at many places. Because now i'm using tags repo instead of blog. So i modified the twig with
{% include 'frontend/page/blog_home.html.twig' with { 'bp':bp|first.blogPosts } %}
Help me on this (twig file): There is only one tag, that's why |first twig filter
Clarify me with this twig filter. Do I'm doing right. Give me suggestion to improve on it. I tried bp[0] This trows error.
Finally: By using old code in controller it returns 14 db hits. Now it returns only 8. Even there are more tags in a post (but old one returns more).

How insert very reference in doctrine

I have 3 entities: User, Page, Post
In the Post entity:
class Post
{
/**
* #ORM\ManyToOne(targetEntity="Page")
*/
private $page;
/**
* #var ArrayCollection $subscribers Specify users that see this post.
*
* #ORM\ManyToMany(targetEntity="User", inversedBy="wallPosts")
*/
private $subscribers;
}
In the Page enity:
class Page
{
/**
* #ORM\ManyToMany(targetEntity="User")
*/
private $members;
}
When a post is published I want that all users that are members of Page are set as subsribers of Post
$post->setSubscribers($post->getPage()->getMembers())
note: Page can have several thousand users, what is best way that have best performance? Should I use a native query, database triggers or procedures?
You can do it in cycle. Subscribe 50 users per one iteration to new post.
Do not forget to clean doctrine internal storage. The main idea is
$page = $post->getPage();
for ($i = 1;; ++$i) {
$members = $page->getPagindatedMembers($i);
if (empty($members)) {
break;
}
foreach ($members as $member) {
$post->addSubscriber($member);
}
$em->flush();
$em->clear(Member::class);
}

CakePhp TranslateBehavior, validate and save multiple locale

Context:
I Want to create a web application using CakePhp which should be translatable. I want to save multiple translations for the same field in one form.
Problem:
I've tried a dozen ways to get this to work and I did. But I ended up using two custom SQL queries which really doesn't feel like a cakePhp solution.
Question:
Does anybody know a better way to achieve the same result?
What I tried:
Giving the form fields a name like 'Model.fieldName.locale', which gives it the right format in the name attr of the input element but then my validation doesn't recognize the field name. But saving works.
Giving the form fields a name like 'modelLocale' and pass in a name attr 'data[Model][field][locale]', in this case the validation works exept for isUnique but saving to the database doesn't work.
More variations of this but not worth mentioning.
I'll add my view and model below: (if u want to see more code or need more info just ask)
/App/View/Category/add.ctp
<?php echo $this->Form->create(); ?>
<?php echo $this->Form->input('title|dut'); ?>
<?php echo $this->Form->input('title|eng'); ?>
<?php echo $this->Form->input('title|fre'); ?>
<?php echo $this->Form->input('description|dut', array('type'=>'textarea')); ?>
<?php echo $this->Form->input('description|eng', array('type'=>'textarea')); ?>
<?php echo $this->Form->input('description|fre', array('type'=>'textarea')); ?>
<?php echo $this->Form->end('add'); ?>
/App/Model/AppModel.php
<?php
App::uses('Model', 'Model');
class AppModel extends Model {
/**
* Check Unique
*
* Searches the i18n table to determine wetter a field is unique or not.
* Expects field name to be as following: "fieldname|locale".
*
* #param array $data The data of the field, automatically passed trough by cakePhp.
* #param string $field The name of the field, which should match the one in the view.
* #returns boolean
*/
public function checkUnique($data, $field) {
// Seperate the field key and locale which are seperated by "|".
$a = preg_split('/[|]/', $field, 2);
// If field key and locale are found...
if (is_array($a) || count($a) === 2) {
$q = sprintf("SELECT * FROM i18n WHERE i18n.locale = '%s' AND i18n.model = '%s' AND i18n.field = '%s' AND i18n.content = '%s' LIMIT 1",
Sanitize::escape($a[1]),
Sanitize::escape(strtolower($this->name)),
Sanitize::escape($a[0]),
Sanitize::escape($data[$field])
);
if ($this->query($q)) {
return false;
}
return true;
}
}
/**
* Setup Translation
*
* Loops trough the fields. If a field is translatable
* (which it will know by it's structure [fieldname]|[locale])
* and has the default locale. Then it's value will be stored
* in the array where cake expects it
* (data[Model][fieldname] instead of data[Model][fieldname|defaultLocale])
* so that cake will save it to the database.
*
* In the afterSave method the translations will be saved, for then we know
* the lastInsertId which is also the foreign_key of the i18n table.
*/
public function _setupTranslations() {
foreach($this->data[$this->name] as $key => $value) {
$a = preg_split('/[|]/', $key, 2);
if (is_array($a) && count($a) === 2) {
$languages = Configure::read('Config.languages');
if ($a[1] === $languages[Configure::read('Config.defaultLanguage')]['locale']) {
$this->data[$this->name][$a[0]] = $value;
}
}
}
}
/**
* Save Translations
*
* Saves the translations to the i18n database.
* Expects form fields with translations to have
* following structure: [fieldname]|[locale] (ex. title|eng, title|fre, ...).
*/
public function _saveTranslations() {
foreach($this->data[$this->name] as $key => $value) {
$a = preg_split('/[|]/', $key, 2);
if (is_array($a) && count($a) === 2) {
$q = sprintf("INSERT INTO i18n (locale, model, foreign_key, field, content) VALUES ('%s', '%s', '%s', '%s', '%s')",
Sanitize::escape($a[1]),
Sanitize::escape(strtolower($this->name)),
Sanitize::escape($this->id),
Sanitize::escape($a[0]),
Sanitize::escape($value)
);
$this->query($q);
}
}
}
/**
* Before Save
*/
public function beforeSave() {
$this->_setupTranslations();
return true;
}
/**
* After Save
*/
public function afterSave() {
$this->_saveTranslations();
return true;
}
}
/App/Model/Category.php
<?php
class Category extends AppModel {
public $name = 'Category';
public $hasMany = array(
'Item'=>array(
'className'=>'Item',
'foreignKey'=>'category_id',
'order'=>'Item.title ASC'
)
);
var $actsAs = array(
'Translate'=>array(
'title',
'description'
)
);
public $validate = array(
'title|dut'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|dut'),
'message'=>'Titel reeds in gebruik'
),
),
'title|eng'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|eng'),
'message'=>'Titel reeds in gebruik'
),
),
'title|fre'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|fre'),
'message'=>'Titel reeds in gebruik'
),
),
);
}
?>
NOTE: There isn't that much information out there on this subject... I have a lot more questions about the translation behavior like getting the recursive results also in the correct locale, ... Anybody know a good tut or source of info (cookbook is quite limited)
Thanks for reading!!
It appears you may be building a CRM of sorts that allows the users to establish content that is read into the site based on the language they have set. I would use the built in i18n and l10n. It makes it really simple, but this is probably not a solution for dynamic content.
Having said that, the only other way I can think of doing this is very tedious. I would build a single screen with a language identifier drop down. So instead of trying to cram ALL languages in the same screen with a test box for each language, I would create one form and then use a drop down for the language.
Your model is using a column to define with language the row belongs to. The form you have created is expressing all languages in a single row. So if you were to view the Index page showing the records, of course you would see:
title 1 eng
title 1 dut
title 1 fre
title 2 eng
title 2 dut
title 2 fre
...
Further more, if you were ever to add a new language, you will have to modify the validation in the model and the form.
However, if you are set on doing it this way, change the | to _ and off you go. But then you will need to store all of the data in a single record. So when you look at the Index for the records, you will see:
title 1 end dut fre
title 2 end dut fre
...
My Advice:
1) Use the built in i18n / l10n using .po / .pot files.
2) If the content will be changing frequently and required to be stored in the database so it can be easily changed / updated frequently on the fly, then use a drop down.
Language: dropdown
Title: text_field

Calling stored procedure in codeigniter

I am using latest codeigniter and trying to call stored procedure from my model. Also I am using mysqli as database driver. Now I am having an error when I call two stored procedures. Following is the error:
Error Number: 2014
Commands out of sync; you can't run this command now
call uspTest();
Filename: E:\wamp\www\reonomy-dev\system\database\DB_driver.php
Line Number: 330
Note that when I call a single stored procedure it works fine. Here is the code for model.
class Menus_model extends CI_Model {
function __construct()
{
parent::__construct();
}
public function getMenus()
{
$query = $this->db->query("call uspGetMenus()");
return $query->result();
}
public function getSubMenus()
{
$query = $this->db->query("call uspTest()");
return $query->result();
}
}
Here is the code from controller
class MYHQ extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('menus_model');
}
public function index()
{
$menu = $this->menus_model->getMenus();
$submenu = $this->menus_model->getSubMenus();
}
}
Is there any solution without hacking the core of codeigniter??
I follow the blog of Mr. Tim Brownlaw:
http://ellislab.com/forums/viewthread/73714/#562711
First, modify application/config/config.php, line 55.
$db['default']['dbdriver'] = 'mysqli'; // USE mysqli
Then, add the following into mysqli_result.php that is missing this command for some strange reason (under /system/database/drivers/mysqli/mysqli_result.php).
/**
* Read the next result
*
* #return null
*/
function next_result()
{
if (is_object($this->conn_id))
{
return mysqli_next_result($this->conn_id);
}
}
Then, in your model, add $result->next_result().
Below is my example.
function list_sample($str_where, $str_order, $str_limit)
{
$qry_res = $this->db->query("CALL rt_sample_list('{$str_where}', '{$str_order}', '{$str_limit}');");
$res = $qry_res->result();
$qry_res->next_result(); // Dump the extra resultset.
$qry_res->free_result(); // Does what it says.
return $res;
}
Having the same problem I found another approach which doesn't change the core, but instead uses a small helper.
Edit: The below linked asset is nowhere to be found.
See CoreyLoose post.
https://ellislab.com/forums/viewthread/71141/#663206
I had to make a small adjusment to his helper though. The line
if( get_class($result) == 'mysqli_stmt' )
could possibly produce a warning since the $result sometimes is passed as a boolean. I just put a check prior to this line and now it works perfectly, with no tinkering with the core!
This seems to be a bug in CodeIgniter. How come it's still in there is beyond me.
However, there's a couple of ways to overcome it.
Check here: http://codeigniter.com/forums/viewthread/73714/
Basically, you modify mysqli_result.php to include next_result() function and make sure to call it after every stored proc. call.
Just note that it assumes you're using mysqli as your DB driver... but you can probably do something similar with any other. You can change your driver in /application/config/database.php It's the line that says
$db['default']['dbdriver'] = 'mysql';
by default. Change it to:
$db['default']['dbdriver'] = 'mysqli';
You could also just close/reopen a DB connection between the calls, but I would definitely advise against that approach.
change dbdriver to "mysqli"
put this function to your model and use it to call stored procedure
function call( $procedure )
{
$result = #$this->db->conn_id->query( $procedure );
while ( $this->db->conn_id->next_result() )
{
//free each result.
$not_used_result = $this->db->conn_id->use_result();
if ( $not_used_result instanceof mysqli_result )
{
$not_used_result->free();
}
}
return $result;
}

session_regenerate_id and database handler

i am using database handler for my sessions which working fine but now i stack into a problem on authentication.
When user login with username/password i do session_regenerate_id and after that i am trying to select the current session_id.
Here is my code
session_regenerate_id();
echo $checkQ=" SELECT * FROM my_sessions WHERE id='".session_id()."' ";
......
but i dont get any results. The session_id is the correct one.
After finish load the page and copy paste the SQL Command to phpMyAdmin i get the results.
I know thats its stupid but the only reason i can think of is that session_regenerate_id() "is too slow" so when i try to read the session_id at next line the session_id has not created in database yet.
Can anyone help me!
I know it has been a while, I hope you have found an answer since this was posted, but I'll add my solution for posterity's sake.
The call to session_generate_id() will cause the value of session_id() to change:
<?php
$before = session_id();
session_regenerate_id();
$after = session_id();
var_dump($before == $after); // outputs false
This problem manifested for me because in the session write handler I was doing this (without such bogus method names, of course):
<?php
class MySQLHandler
{
function read($id)
{
$row = $this->doSelectSql($id);
if ($row) {
$this->foundSessionDuringRead = true;
}
// snip
}
function write($id, $data)
{
if ($this->foundSessionDuringRead) {
$this->doUpdateSql($id, $data);
}
else {
$this->doInsertSql($id, $data);
}
}
}
The write() method worked fine if session_regenerate_id() was never called. If it was called, however, the $id argument to write() is different to the $id passed to read(), so the update won't find any records with the new $id because they've never been inserted.
Some people suggest to use MySQL's "REPLACE INTO" syntax, but that deletes and replaces the row, which plays merry havoc if you want to have a creation date column. What I did to fix the problem was to hold on to the session ID that was passed to read, then update the session ID in the database during write using the id passed to read as the key:
<?php
class MySQLHandler
{
function read($id)
{
$row = $this->doSelectSql($id);
if ($row) {
$this->rowSessionId = $id;
}
// snip
}
function write($id, $data)
{
if ($this->rowSessionId) {
$stmt = $this->pdo->prepare("UPDATE session SET session_id=:id, data=:data WHERE session_id=:rowSessionId AND session_name=:sessionName");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':rowSessionId', $this->rowSessionId);
$stmt->bindValue(':data', $data);
$stmt->bindValue(':sessionName', $this->sessionName);
$stmt->execute();
}
else {
$this->doInsertSql($id, $data);
}
}
}
I think I'm having the same problem you are having. It's unclear to me whether this is a PHP (cache) feature or a bug.
The issue is that, when using a custom SessionHandler and calling session_regenerate_id(true), the new session is not created until the script terminates. I have confirmed that by doing the same thing you did: SELECTing the new session id from the database. And the new session is not there. However, after the script finishes, it is.
This is how I fixed it:
$old_id = session_id();
// If you SELECT your DB and search for $old_id, it will be there.
session_regenerate_id(TRUE);
$new_id = session_id();
// If you SELECT your DB for either $old_id or $new_id, none will be there.
session_write_close();
session_start();
// If you SELECT your DB for $new_id, it will be there.
Therefore the solution (workaround) I came about was to force PHP to write the session. I hope this helps.