Showing posts with label PHP. Show all posts
Showing posts with label PHP. Show all posts

Thursday, 21 November 2013

SQL update multiple rows with a single query, different value per row

Problem:

I'd like to update many rows with each row having a possibly different value, while using only a single SQL query. A query with a single WHERE clause would be inadequate as a result.

Warning:

This how-to can possibly ruin a database if done wrong, so make sure that you first test it using toy data (and make sure it works properly). Use at your own risk!

Please feel free to point out any typos and mistakes in the comments below.

Solution:

This is a summary (for myself) of the solution found at Karl Rixon's page here. For greater detail, please follow the aforementioned link.

Example table:

We'll use the following table for this example:

Table name: 'membership_list'
IDS_IDNameCountryOne_or_zero
1205G0BobUSA1
2205G0CatUK0
3319E8JaneAustralia1
4518A1JoeIceland1
...............

Example 1 - multiple rows, single column:

In order to make multiple updates, you can use a CASE block in SQL combined with an appropriate WHERE clause to select the appropriate rows and set the different values. For example, in order to update the column `Country` based on column `ID` alone:

UPDATE `membership_list`
  SET `Country` = CASE `ID`
    WHEN '1' THEN 'Antarctica'
    WHEN '3' THEN 'Canada'
  END
WHERE `ID` IN (1,3);

Do NOT forget the WHERE clause otherwise all other values will be set to NULL.

Example 2 - multiple rows, multiple columns:

In order to change more than one column, more than one CASE blocks can be used. For example, in order to both the columns `Country` and `One_or_zero` based on `ID` alone:

UPDATE `membership_list`
  SET `Country` = CASE `ID`
    WHEN '1' THEN 'Antarctica'
    WHEN '3' THEN 'Canada'
  END,
      `One_or_zero` = CASE `ID`
    WHEN '1' THEN '1'
    WHEN '3' THEN '1'
  END
WHERE `ID` IN (1,3);

Example 3 - multiple rows, multiple WHERE conditions:

Now assuming that you only want to alter all rows in `membership_list` that have '205G0' as the `S_ID` and 'Bob' as the `Name`, and we want to set all `Country` to unique values based on `One_or_zero`:

UPDATE `membership_list`
  SET `Country` = CASE `One_or_zero`
    WHEN '1' THEN 'Switzerland'
    WHEN '0' THEN 'Russia'
  END
WHERE `One_or_zero` in (0,1) 
  AND `S_ID`='205G0'
  AND `Name`='Bob';

Example 4 - constructing a PHP query for Example 1:

Among other benefits, a single query for multiple updates becomes particularly useful when using server-side scripting such as PHP, as there is a significant gain of speed when compared to looping through many single queries.

Here is an example script to construct the query for the first example above:

$arr_id_country = array(
    '1' => 'Antarctica',
    '3' => 'Canada'
  );

$str_ids = implode(',', array_keys($arr_id_country));

$str_when_then = "";
foreach($arr_id_country as $id => $country) {
  $str_when_then .= sprintf(" WHEN '%d' THEN '%s' ",
      $id,
      $country // note, you'd sanitize this if from user input
  );
}

// whitespace + appends included in example for readability
$template =   "UPDATE `membership_list` "
            . "   SET `Country` = CASE `ID` "
            . "     %s "
            . "   END "
            . " WHERE `ID` IN (%s);"

$query = sprintf($template, $str_when_then, $str_ids);

// do database queries, etc., here ...

Notes:

Once again, do NOT forget to include the WHERE clause that contains all the values found in the CASE block in order to prevent accidentally affecting other rows unintentionally.

References:

Thursday, 15 November 2012

Workaround for Symfony 2.1 composer.phar update timezone error

Problem:

Every time I run "php composer.phar update", I get an error similar to:

Warning: date_default_timezone_get(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'America/Los_Angeles' for 'PST/-8.0/no DST' instead in /path/to/symfony21/project/vendor/monolog/monolog/src/Monolog/Logger.php line 112

Script Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache handling the post-update-cmd event terminated with an exception

[RuntimeException] An error occurred when executing the "'cache:clear --no-warmup'" command.

What's worse is that my php.ini file has a timezone already set. For instance, when I run the shell command "php -i | grep date.timezone", a timezone is produced, e.g. "date.timezone => America/Los_Angeles => America/Los_Angeles".

Workaround:

A quick workaround to this issue is to explicitly set a timezone in the app/console script. Open the app/console script in a text editor. In app/console, near the top (for instance, right before set_time_limit(0);), add the following line, replacing the example timezone from below with one of the valid PHP timezones found on the PHP docs page:

date_default_timezone_set('America/Los_Angeles');

When you save your changes to app/console and run "php composer.phar update" once more, it should now work.

Notes/Disclaimer:

This workaround isn't by any means an ideal fix, and it will affect the timezone of all Symfony2 commands run using the app/console script. (A fortunate side-effect, however, would be the workaround of timezone issues in other scripts using app/console.) This workaround may or may not also affect any scripts that try to upgrade app/console. To reverse this change, simply remove the "date_default_timezone_set" line you inserted into app/console. (Or even better, prior to modifying app/console, copy the old app/console script, rename the copy to app/console.old, and replace the script with the copy if you need to revert.) This workaround was tested with Symfony 2.1.3, PHP version 5.3.6 using MAMP 2.0.5 under OS X 10.7. Your mileage may vary with other versions.

Wednesday, 7 November 2012

PHP timezone error

Problem:

When trying to make a new DateTime object using PHP, or use any built-in time-related functions, I keep getting the following exception thrown: "DateTime::__construct(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'America/New_York' for 'EST/-5.0/no DST' instead"

Solution:

This error means that a PHP script should have some method of having its default timezone set besides the unsafe method of using the system's setting (making for more predictable and consistent behaviour of the script).

In the case of PHP, you can either use date_default_timezone_set() somewhere prior to calling any date or time functions. Alternatively, you can edit the /etc/php.ini file to add a valid timezone to the date.timezone parameter (e.g. date.timezone = "America/New_York") so that all scripts will use this timezone unless set otherwise by using date_default_timezone_set().

Reference:

Tuesday, 6 November 2012

Increase phpmyadmin import file size limit

Problem:

I'd like to increase the import file size limit for phpmyadmin.

Solution:

The import size limit of phpmyadmin is the file upload size limit of your php configuration, so the most direct method of increasing this limit is to increase the upload limit in your php.ini file.

If you have shell access with root privileges, (or high enough privileges,) you can follow these steps in the terminal to increase this limit:

  • find your php.ini file if you don't know its location, you can find it by the shell command "whereis php.ini"
  • open your php.ini file and search for the line that contains "upload_max_filesize". It may look something like "upload_max_filesize = 2M"
  • change this to the size that you need, e.g. "upload_max_filesize = 8M"
  • you may need to restart your apache server to see the change

Notes:

Note that by performing this change, the upload limit of all php scripts, not only phpmyadmin will be increased.

Saturday, 16 June 2012

Get the current route and url in a Symfony 2 controller

Problem:

How do I get the current page's URL and current route in a Symfony 2 controller?

Solution:

There are several ways to get the current page's route and url. One way is demonstrated here.

Current route

public function someAction(Request $request){
  // ... some code here

  $currentRoute = $request->attributes->get('_route');

  // more code ...
}
Current URL

public function someAction(Request $request){
  // ... some code here
  
  $currentRoute = $request->attributes->get('_route');
  $currentUrl = $this->get('router')
                     ->generate($currentRoute, array(), true);
  
  // more code ...
}
Updated:

Alternatively, you can also generate the current URL directly without using the current route (thanks COil for the comment!)


$currentUrl = $this->getRequest()->getUri();

$currentUrl = $request->getUri();
//(see comments below, thanks Marcos for the comment!)


Notes:

This was tested to work using Symfony2 version 2.0.4.

Reference:

http://symfony.com/doc/current/book/routing.html#generating-urls

Include custom PHP-generated html code using Twig and Symfony 2

Problem:

How do I embed custom HTML code (or scripts, etc.) into a page generated from a Twig template?

This is useful because:

For instance, if a page controller in Symfony 2 is used to generate a fancy widget, such as a calendar, you may want to have a way for the twig template to render this custom server-side generated code.

Solution:

Pass the customized html string into a parameter in the twig template (customcodeparam, in the example below), and use the raw filter when rendering, e.g.

{{ customcodeparam | raw }}

Detailed example:

This example is not 100% complete, but it should include enough code to convey the idea of how to embed custom code into a twig template. This example assumes that you're already familiar with PHP and Symfony 2, and are learning Twig.

Imagine that you have a twig template named 'something.twig':


{# MyBundle:Page:something.twig #}
{% extends 'MyBundle:Page:index.html.twig' %}

{% block body %}
<h1>My awesome custom server-side-generated widget</h1>
{{ customcodeparam | raw }}
{% endblock %}

And also imagine that there was a page controller that rendered the template:


public function renderSomethingAction(){

  // replace the code below with your own
  $widgetcode = "<a href='http://www.google.ca'>Go Google!</a>";

  try {
         return $this->render('MyBundle:Page:something.html.twig',
                    array(
                        'customcodeparam' => $widgetcode
                    ));
  } catch (\Doctrine\ORM\NoResultException $e) {
         return null;
  }
}

The resulting rendered html in the body block would look similar to the following:


<h1>My awesome custom server-side-generated widget</h1>
<a href='http://www.google.ca'>Go Google!</a>

(Note that the above code snippet does not include the html generated by MyBundle:Page:index.html.twig)

Important

This probably goes without saying, but just a reminder that when including un-sanitized code that is generated by your server-side script on your web app, you should ensure that this code is safe. This is especially important to consider if the inserted code includes user-generated content (in case a user decides to sneak in a script, etc.). In other words, when using the trick in this post, the sanitization of the page is now up to you and not up to Twig. Pretty basic, I know, but I thought I should mention it anyway.

Wednesday, 9 May 2012

String character whitelist

Problem:

How do I determine if a string contains only allowed characters?

Solution (code snippet):

The following PHP example code will check if a string contains only characters in a white-list. It should be pretty easy to convert this to other languages.

function containsOnly($string, $whiteliststring){
    $char_arr = str_split($string);
    $whitelist_arr = str_split($whiteliststring);
    
    foreach($char_arr as $char){
        if(in_array($char, $whitelist_arr)==false) return false;
    }
    return true;
}

Example usage of this would be, for instance, checking for numbers only:

$numbers = "0123456789";
$not_only_numbers = "abcd123";
$only_numbers = "123";

echo containsOnly($not_only_numbers, $numbers)?"yes":"no"; // no
echo containsOnly($only_numbers, $numbers)?"yes":"no";     // yes

Code explained:

The containsOnly function uses str_split to convert strings into arrays containing their characters. The loop checks to see if any of the test string characters is absent from the white-list.

This code can generally be sped up by using an implementation of a set rather than arrays.

Sunday, 22 April 2012

Time and time zones in PHP (and SQL)

Question:

How do I store, and then show the proper times at different time zones with php and SQL?

Solution:

Working with timestamps and time zones can be initially confusing with PHP. A common, but misguided solution when storing times in a database involves offseting a time by a number of hours, depending on what time zone you are in. This will lead to confusion later.
The best way to go about times and time zones is to use Unix time stamps. These are always measured in time passed since midnight, January 1, 1970 GMT, regardless of where you are in the world.
For instance, a time stamp of 1335092594 will be Sun, 22 Apr 2012 04:03:14 -0700 in PST (e.g. Seattle, WA), Sun, 22 Apr 2012 11:03:14 +0000 in UTC (e.g. Grenwich, United Kingdom), and Sun, 22 Apr 2012 21:03:14 +1000 in Sydney, Australia.

Example:

  1. To get the current time with PHP, you can use the time() function
  2. To get the date of your server, use date()
  3. To get the date at UTC, use gmdate()
  4. To change timezones in order to render that zone's local time, use date_default_timezone_set() followed by date()
e.g.:
$timestamp_now = time();
echo ' My Server Time: ' . date('r', $timestamp_now) . PHP_EOL;
echo ' UTC: ' . gmdate('r', $timestamp_now) . PHP_EOL;
date_default_timezone_set('Australia/Sydney');
echo 'Sydney, Australia: ' . date('r', $timestamp_now) . PHP_EOL;

This should yield (at a time stamp of 1335092594):
My Server Time: Sun, 22 Apr 2012 04:03:14 -0700
UTC: Sun, 22 Apr 2012 11:03:14 +0000
Sydney, Australia: Sun, 22 Apr 2012 21:03:14 +1000

Using SQL, you'll want to simply save this time stamp whenever you want to record a time, regardless of where in the world you are. A caveat is that you will want to make sure that your server is set to the correct time zone so it records the proper time stamp to begin with.

Update:

Just found a really good blog post related to this topic (PHP time and SQL). Enjoy :)
http://www.richardlord.net/blog/dates-in-php-and-mysql