Pages

ADSENSE link unit (728 X 15px) SPACE

Countryside Gazebos

Saturday, September 10, 2011

NEATXD

The new http://www.projectshed.com.au/ website is the place to share your dream project with other car builders, enthusiasts and anyone thinking about starting their own build-up. Feel free to post photos, share tips, exchange ideas and even find an answer to that perplexing problem...

Valvoline are proud to sponsor ProjectShed and will support chosen car builders with free Valvoline products and you might even have your project featured in a Valvoline adverstising campaign!

It only takes 30 seconds to join and it's free, so get into ProjectShed now!


Gazebo shed on saleDownload here
Read More ...

Friday, September 9, 2011

Ford Taunus G.T. Coupe

My dad found this German car in the long grass at a wreckers yard in Darwin and had it sent down to me in Perth.This is the only Ford Taunus G.T.Coupe in Australia(There is a GXL in Alice Springs) These cars are built on the same platform as a Cortina(TC/TD) so converting it from left to right hand drive was as simple as buying a donor car and swapping the firewall over.The front cross member, steering and suspension was a bolt in ,bolt out deal dead f# ken easy. This was ment to be a father and son project but my father fell ill and it was sold off and hasn't been seen since


Gazebo shed on saleDownload here
Read More ...

48-350 (FJ Holden with EFI 350)

A paddock find in late 2008, a 1954 FJ Holden that I intend a comprehensive resto-mod including updated brakes, suspension, running gear, fuel injected 350.

Progress has been slow due to competing projects, but the scribbled plans, thinking and planning has been thorough! The back half of 2010 should see a strong start on the project with the aquisition of a 'stunt double' - a 1951 FX Holden body to trial all modifications on before any mods are made to the near exceptional 1954 FJ body.


Gazebo shed on saleDownload here
Read More ...

Toyota serie

The new http://www.projectshed.com.au/ website is the place to share your dream project with other car builders, enthusiasts and anyone thinking about starting their own build-up. Feel free to post photos, share tips, exchange ideas and even find an answer to that perplexing problem...

Valvoline are proud to sponsor ProjectShed and will support chosen car builders with free Valvoline products and you might even have your project featured in a Valvoline adverstising campaign!

It only takes 30 seconds to join and it's free, so get into ProjectShed now!


Gazebo shed on saleDownload here
Read More ...

GM Chevette

even here where everything is hard, where everything is expensive and some things are nearly impossible can still make cars with a nice finish but of course always with first quality materials and workmanship ditto .. that's always innovating and Custom DPM is a Made in Brazil


Gazebo shed on saleDownload here
Read More ...

"PANIC" XP Coupe

The new http://www.projectshed.com.au/ website is the place to share your dream project with other car builders, enthusiasts and anyone thinking about starting their own build-up. Feel free to post photos, share tips, exchange ideas and even find an answer to that perplexing problem...

Valvoline are proud to sponsor ProjectShed and will support chosen car builders with free Valvoline products and you might even have your project featured in a Valvoline adverstising campaign!

It only takes 30 seconds to join and it's free, so get into ProjectShed now!


Gazebo shed on saleDownload here
Read More ...

XC 351 Sundowner Panelvan

I purchased this in Darwin as I found out that they would not register my V8 Hilux 4x4 in Qld. so I needed something that could pull my tandem axle caravan. I was quite impressed with the van finding the only down side was was a drumming noise when driving, but that was easily fixed by turning the stereo up. The van got me all the way back to Perth. Not having much use for a van I advertised it for sale. I had a guy ring up for it on his mobile and he said that he was driving up from Albany and if it was as good as I said it was he said that he would take it and if I held it untill he got there he would give me a extra $100.00 so I said yes as nobody else had rang up(I didn't tell him that). A few hours later a bikie looking dude rocks up in a LC/LJ Torana, checks out the van, loves the extra "storage" area next to the fuel tank, so he goes to his car pulls out a brief case that when he opens it it's full of cash, all in $20.00's. He throws a bundle of cash at me and says "count it" and true to his word he gave me a extra $100.00 He then hopped in the van and was about to drive off to Kalgoorlie and I said what about your Torana? he says the rego is about to expire", throws me the keys and says" you keep it". Then he drove off .Not knowing what to do next I rang my mate that was into Torana's and told him all. He did a Revs check and it came up clean so I let him have it, he was stoked. A few months later I get a letter from the cops stating the rego on the van had expired and they wanted the plates back.I told the cops that I had sold it. He never transfered the van into his name or paid the rego and that was the last I heard of the van.


Gazebo shed on saleDownload here
Read More ...

Thursday, September 8, 2011

Robs KB Laser

Robs KB Laser

this is my ford laser My wife and I are doing it up for summernats in 2 years time it currently has a venom 85w speaker system installed and an orion amplifier speakers are mounted upside down in a custom box which is yet to be carpeted and twin red undercar leds and a jvc head unit


Gazebo shed on saleDownload here
Read More ...

Comparison of MyISAM and InnoDB MySQL Databases

It's not breaking news that MySQL is by far the most popular open-source RDBMS that exists today. The widespread adoption of the database server stems from a balance of robustness and solid performance; it doesn't hurt to that it also supports a variety of storage engines, which gives users the ability to pick the one that best suits their needs.

Unfortunately, not everyone using MySQL is an experienced DBA with a deep background on each storage engine supported by the server. This is especially evident in the terrain of web development, where (in many cases) the design of a project?s database schema is conducted by taking into account only the kind of data that will be persisted between HTTP requests.

The bright side is that in most use cases, selecting the appropriate storage engine for a particular project can be much simpler than one might think. If you?ve been working with MySQL on a frequent basis and have followed its evolution over time, you probably know that the whole selection process can be reduced to a few typical options: the older MyISAM system and the newer, flashier InnoDB.

That being said, in this tutorial I?ll be conducting a comparison between the main characteristics offered by MyISAM and InnoDB, in this way making it easier for you to spot their differences without having to get lost in the overwhelming contents of the MySQL manual.

MyISAM

Being the storage engine included by default with MySQL from its early days, MyISAM (short for My Indexed Sequential Access Method) is a relatively simple storage mechanism (especially when compared to more complex and sophisticated implementations, such as InnoDB and DB2) which has a fast performance, due to the fact that each record is organized first into  a sequential file, and then accessed via a set of small indexes or pointers annexed to the record in question.

It?s worth noting, however, that this simplicity and great performance comes with a hidden cost: first and foremost, MyISAM doesn?t provide support for transactional ACID operations, meaning that it?s not possible to run batches of operations in one go, or even to roll them back when something goes wrong. For obvious reasons, this can lead to potential inconsistencies in tables and affect the integrity of the stored data, thus making it necessary to tackle this and other related issues at application level.

In addition, when accessing a database, MyISAM uses an approach known as table-level blocking. Simply put, this implies that each time the server attempts to run an operation in the target table, it will be blocked during the execution process. While at first glance this operational mode seems to be quite efficient, it can quickly become a poor strategy, particularly when it?s necessary to attend to multiple clients trying to gain access to the same resource at the same time (aka concurrence issues).

Finally, there?s another big "NO" that MyISAM hides under the hood. Effectively, the mechanism doesn?t offer support for handling native foreign keys. Of course, I?m not saying that you won?t be able to create two or more tables and relate them manually at will through a few foreign keys. What I actually mean is that you can?t specify foreign keys in the corresponding table definitions and specify the constraints that they will behave in response to common cascade operations, such as updates and deletions.       

In summary: MyISAM will perform satisfactorily as long as you?re database requirements remain basic and limited to running traditional, non-transactional CRUD operations. If you need to take a step further into the realm of transactions (which goes hand in hand with the trends of modern application development), it should be discarded in favor of a more robust implementation, such as InnoDB.

InnoDB

Added to MySQL in 2005, InnoDB (whose manufacturer is the Finnish company Innobase Oy [http://www.innodb.com]) is a remarkable improvement of its predecessor MyISAM, which not only saves records following the same order of their primary keys (something that sometimes avoids an additional reordering process), but it also offers solid support for performing transactional CRUD operations (the details on how InnoDB achieves this are out of the scope of this article, but you can take a deeper look at them here [http://dev.mysql.com/doc/refman/5.0/en/innodb-storage-engine.html]).

While the support for transactions is hard to beat when it comes to selecting the storage engine that fits your needs most efficiently, InndoDB provides a few other robust features that turn it into an even more appealing contender.

If you?re wondering what these extra features are, here?s a quick summary of them: first off, the engine implements a row-level blocking mechanism (unlike its older brother MyISAM, which blocks the entire table being accessed), meaning that the server will be able to perform concurrent operations in the same table, as long as they don?t target the same row. This is a really clever approach, which really shines on its own, especially if you have a lot of clients accessing the same target table.

Last but not least, InnoDB will let you easily specify (in the same table definition) which column(s) will be native foreign keys, as well as the behavior that they will have when updating and deleting rows in the parent table(s) (in other words, the foreign keys constraints mentioned in the preceding section).

Don?t worry - I?m not planning to cover how to define foreign keys constraints when using InnoDB; however, if you?re interested in learning more on this topic, just check out the tutorial that I wrote here (http://www.devshed.com/c/a/PHP/Updating-and-Deleting-Records-in-MySQL-Tables-with-Foreign-Key-Constraints/).

It goes without saying that MyISAM and InnoDB implement a host of additional low-level features, which are way to extensive to dicuss here. Nevertheless, if you only need a quick guide that will show you the most relevant facilities that each storage engine brings to the table, hopefully this tutorial will assist you in the analysis process.

Final Thoughts

Over the course of this article, I made a brief summary of the most relevant features offered by the popular MyISAM and InnoDB MySQL storage engines. As you saw through this quick review, the clear winner here is undoubtedly the latter, not only for being much newer than its competitor, but due to the fact that it provides a bunch of facilities that are hard to ignore when it comes to building database applications, including the support for transactions (a key one, in my humble opinion) and the implementation of a solid row-level blocking mechanism.

Naturally, if your database requirements are rather basic and you don?t need to use transactions, MyISAM is still a good option worth noting that will yield pretty satisfactory results as well. Regardless of this, InnoDB is rapidly becoming the choice of many users, because of the aforementioned benefits, which as I said before, are definitively along the lines of modern application development.    

See you in the next MySQL tutorial!



blog comments powered by

Gazebo shed on saleDownload here
Read More ...

Free Monitoring Tool for Java Apps on Heroku

The announcement came during the Salesforce.com Dreamforce ?11 conference, which brings hundreds of companies and thousands of customers, partners, and developers together to discuss cloud computing.  The timing of the announcement could not be better, as Java support on Heroku was recently made official.  New Relic?s application performance management tool offers a comprehensive solution for monitoring web applications and is used by a host of businesses, both large and small. 


The New Relic Standard edition provides 24/7 app and user monitoring and is ideal for smaller projects.  It supplies users with detailed information on database performance through various statistical tracking measures.  Customer data is retained for 1 week, and there can be unlimited users per account.  The tool also comes equipped with a JVM performance analyzer, incident and error alerting, plus much more to ensure that web application performance is optimized.


Heroku VP of Product Management Oren Teich commented on the importance of the latest move: "As more businesses look to Heroku to enable their cloud strategy, providing application performance monitoring is critical.  We are excited to announce the availability of New Relic's add-on for our tens of thousands of developers worldwide and now growing base of Java developers with New Relic's new production profiling and monitoring capabilities integrated seamlessly with Heroku."


Bill Lapcevic, New Relic?s VP of Business Development, noted the strength of the partnership between New Relic and Heroku, stating: "New Relic and Heroku have worked together to deliver the tools, resources, and skills organizations needed to rapidly and successfully deploy business-critical apps in the cloud.  The addition of New Relic's app production profiling and monitoring capabilities to Heroku's new Cedar platform underscores our continued joint commitment to providing Java developers with the same scalability, simplicity, and reliability that Ruby programmers have enjoyed for over the last two years." 


New Relic Standard edition is available for free to Heroku?s Java customers and continues to be free of charge to those running Ruby apps on the Heroku platform.  New Relic also offers its application performance management tool in a Pro edition that offers additional features beyond those found in the Standard version.  A 14-day trial can be used to experience the Pro edition?s capabilities.  A business package is available as well for clients with more than five servers.  More information on the different offerings can be found on New Relic?s website, www.newrelic.com.


Formore on this topic, visit http://www.marketwatch.com/story/new-relic-provides-developers-with-free-24x7-web-app-monitoring-and-analysis-for-java-apps-on-heroku-2011-08-30?reflink=MW_news_stmp


Extentech Releases Update for its Java Spreadsheet Solution


Extentech, an industry leader in web spreadsheet and document technology, recently upgraded its portfolio of Java components and development tools with the announcement of its release of ExtenXLS 10.1.  The release upgrades the already solid Java spreadsheet SDK to include several new and improved features.


The ExtenXLS Java spreadsheet toolkit offers a fast and reliable platform that is ideal for reading, writing, and generating Excel spreadsheets.  Beyond its excellent performance and reliability, ExtenXLS? main advantage over competitors is that it now gives users SVG charting that is compatible with web browsers such as Google Chrome, Firefox, Internet Explorer, and Safari.  SVG is an XML graphics format that eliminates the need for any browser plugins, as the most up-to-date browsers support its output.  Thanks to SVG charting, users can experience the benefits of data rich spreadsheet reporting in real time with vector graphics that are not only fast, but scalable as well.  ExtenXLS also offers VML Chart output to provide compatibility with legacy browsers. 


In addition to the SVG charting, ExtenXLS 10.1 is highlighted by several other appealing features, beginning with the introduction of the new ERF, NORMINV, NORMSINV, NORMDIST, and NORMSDIST formula functions.  Sheet protection and locking have been upgraded for increased compatibility with Excel, and charts containing spreadsheets can be exported in PDF format. 


ExtenXLS 10.1 also comes with various improvements aimed at increasing performance.  One of those improvements focuses on the realm of XLSX and OOXML fidelity, which has been increased when working with Excel 2010 and encrypted spreadsheet files.  A new workbook parser has been included as well to offer enhanced event-driven document review for sessions that are read-only.


Extentech CEO John McMahon commented on the latest ExtenXLS release, stating: ?We are continually updating ExtenXLS to provide the most functionality for developers and to ensure that it remains the top choice of enterprises that need reliable and fast spreadsheet capabilities without vendor or platform lock-in.  With our new SVG charting output, we give customers a better way to present spreadsheet data, with rich and up-to-date web-based charts. Our system renders charts very quickly, so busy users don't need to wait for their charts and spreadsheet data to display, allowing everyone to work more efficiently."


blog comments powered by
Gazebo shed on saleDownload here
Read More ...

Back Up a Joomla Site with Akeeba Backup

There is certainly more than one way to backup your Joomla! website, but without a reliable component (or even a built-in one), backing up a Joomla! website is a feat left to more technically savvy individuals. Without a backup component, the only way to reliably backup your Joomla! website is by using complex database and operating system commands.

Luckily, a wonderful backup component for Joomla! has been developed called Akeeba Backup (http://www.akeebabackup.com/). Akeeba Backup is a native Joomla 1.5/1.6 backup component available in two versions: Akeeba Backup Core (free, limited support) and Akeeba Backup Professional (commercial, full support). The free version is--of course--free, and the professional version will cost you--but not much, luckily. In this tutorial, I will walk you through the installation, configuration, backup, and restoration of your Joomla! site with Akeeba Backup Core.

Before you start this tutorial, you will need to meet the following requirements:

An SSH/SFTP/FTP client.
Free vs Professional
As I mentioned previously, Akeeba Backup comes in two versions: a free version (Akeeba Backup Core) and a professional version (Akeeba Backup Professional).

The free version of this component provides all the necessary features of a simple backup system. This includes the ability to perform manual and scheduled backups of the website, define separate backup jobs (using profiles), select multiple backup formats, manage the backup files, and restore the backup files. The free version even has the ability to copy a Joomla! website to another web server using the Site Transfer Wizard.

The professional version of this component provides additional features including the ability to backup to cloud storage (DropBox, Amazon S3, RackSpace CloudFiles and more), encrypt backup archives with AES-128 encryption, include multiple databases and directories outside the default Joomla! directory structure, and filter files, directories, and extensions. While Akeeba Backup Core does provide the ability to schedule backups, Akeeba Backup Professional features a native CRON script which can backup a Joomla! website even if the web server is not running!

With over 2.5 million downloads of Akeeba Backup Core, the free version provides a robust feature set to reliably backup millions of Joomla! websites. Depending on your requirements, you may need to upgrade from the free version of the component to the professional version. But before you start deciding which version is right for you and if you will be able to afford it, let?s begin this tutorial so you can see the full power of Akeeba Backup Core.

Configuring Akeeba Backup Core
Before you begin this tutorial, you will need to make sure you have FTP or SFTP access to your web server because you will need to create a directory for your backups. Use your favorite FTP/SFTP client to connect to your web server and display the root directory of your website. Depending on your web server (or web host), your root directory may resemble any of the following:

/home/username/public_html
/home/username/domain.com
/home/username/domain.com/www
In this example, my web server uses this directory structure: /home/jpraterdemo/domain.com.

If your are unable to access non-web-accessible directories, you will need to ignore this section and proceed to the Performing a Backup section.

*NOTE: While this step is not required, keep in mind that all of your backups will be stored in a web-accessible directory.

By default, Akeeba Backup stores the backup files in the following directory (starting at the Joomla! root directory): /administrator/components/com_akeeba/backup. While this is a perfectly valid backup directory, it is safer to store your backups in a non-web-accessible directory. Remember, all of your Joomla! directories (/administrator, /components, /plugins, /modules, /includes, etc.) are all web-accessible, meaning anyone with an Internet connection and a computer can access them. This is why you create a backup directory outside the root of your web-accessible directories. You can name your backup directory any name you like--just make sure you remember it (and its location) because you will need to access it later in the tutorial. In this example, I have named my backup directory backups.

After you have created a backup directory, it is time to install the component. The Akeeba Backup component is installed just like any other component. Log in as a Joomla! administrator, then from the main menu, go to Extensions > Install/Uninstall. Browse to the location of the component you downloaded earlier (com_akeeba-x.x.x.zip), then click the Upload File & Install button.

After the component has been installed successfully, a summary of all the installed Akeeba components, modules, and plugins will be displayed.

If you go to your site?s Control Panel (from the main menu, Site > Control Panel), you will now see a new backup icon. This icon quickly informs you of your site?s backup status. Now let?s configure Akeeba Backup for the first time by launching the component.

The first time you launch Akeeba Backup, it will ask you to run some post-installation configuration tasks. These tasks are:

Enable System Restore Points: This feature is very useful. Essentially, whenever you reinstall or uprgade any extension, Akeeba Backup will make a mini-backup of the extension?s database tables and physical files. Should an error occur during the extension?s installation, you can easily roll back the extension to its previous state.

Enable automatic Akeeba Backup update emails: This feature will send you an email whenever an updated Akeeba Backup component is available. If you do not regularly check for updated extensions for your Joomla! website, you may want to check this option so you can easily stay updated of new releases.
Run the Configuration Wizard: If this is your first time launching Akeeba Backup, then I strongly recommend you enable this option. This will configure Akeeba Backup with optimal settings for your server and database environment.
Once you have enabled the necessary options, click the Apply these preferences button.

Akeeba Backup will now optimize itself for your operating environment. Do not close the browser or navigate away from the page.

Once the Configuration Wizard completes its tasks, it will prompt you to either backup or configure Akeeba Backup. Before you start a new backup, let?s first take a look at some of the configuration options by clicking the Configuration button.

After running the Configuration Wizard, Akeeba Backup has already made all the necessary configuration changes. As I mentioned previously, you are going to change the default backup directory to a non-web-accessible directory. To change this directory, click the Browse... button in the Output Directory section.

As you can tell from the directory hierarchy at the top of the window, your backups will be stored in a web-accessible directory. To move to a non-web-accessible directory, click the parent directory of your Joomla! installation. In this example, all of my Joomla! files are stored in the /demo.thoughtreactor.com directory; clicking its parent directory will put me in a non-web-accessible directory. This will be the same directory where you created your backup directory earlier.

Now that you are in Joomla!?s parent directory, you should see the backup directory you created earlier. Click the backup directory move into it.

Once in the backup directory, you will see a warning message indicating the directory may not be readable. Depending on your server configuration, PHP scripts (including your Joomla! installation) may not be able to access directories outside of the web-accessible directories. If your scripts can access non-web-accessible directories, ignore this warning. Now, click the Use button at the top of the window.

Now click the Save button to save your changes.

Now that you have finished configuring Akeeba Backup, it is time to perform your first backup.

Control Panel Overview
After saving the configuration changes, you will be redirected to the Control Panel. The Control Panel is self-explanatory--it provides quick access to the overall backup status of your Joomla! website. The left-hand side of the Control Panel contains shortcuts to all the backup and restore functions. The right-hand side contains summary information about your last backup, backup statistics, and news updates from Akeeba. From the Control Panel, you can quickly discover any problems with your backups, perform new backups, restore mini-backups, and even update the component, all from one place!

Performing a Backup

Now it is time to perform your first backup. Since you have made all the necessary configuration changes, backing up your Joomla! website will only take seconds!

From the Joomla! main menu, go to Components > Akeeba Backup. You are now in the Akeeba Backup Control Panel. From here, you can run the configuration wizard, manually edit the configuration, backup your Joomla! website, administer backup files, transfer your website to another web host, and even check for updates. This section will focus on performing a backup, so click the Backup Now icon.

Whenever you perform a backup with Akeeba Backup, an installer is bundled with the backup. This installer is similar to the Joomla! installer. Whenever you perform a recovery of your Joomla! website, you will actually be performing a new installation using Akeeba Backup?s bundled installer.

Akeeba Backup asks you for a few pieces of information before starting the backup. You will notice the top option is Active Profile. Akeeba Backup supports multiple profiles, and each profile is essentially a separate set of configuration options (modifying the Output Directory in the previous section, for instance). While you may find a good use for them, I have found they are only really useful with Akeeba Backup Professional because of its advanced feature set (backing up to Amazon S3, backing up multiple databases, etc.). By default, a single profile is created which is profile #1. If you click the drop-down box, only one profile will be listed. The short description is automatically generated to display the current date. You are not limited to using this pre-filled description; you can enter any text you like. A comment section is provided for situations where a short description is not enough space. Once you have entered any necessary information, click the Backup Now! button.

The backup process will now begin. During the backup, do not close your browser or navigate to another page. The backup list and progress bar will let you know exactly what is going on behind the scenes. Depending on the size of your Joomla! website, this process can last anywhere from a few seconds to 10+ minutes. The smaller the website, the quicker the backup process, and vice versa.

Once your backup completes successfully, you have the option of viewing the log or administering the backup files. Click the Administer Backup Files button so you can see a list of all of your backups.

You will now see a list of all the backups performed by Akeeba Backup. You can click most of the column headings to sort by a particular column, and description and date search boxes are provided to further filter your backup results. If you recall from the previous section, whenever you update or reinstall an extension, a mini-backup is performed by Akeeba Backup. You can access these mini-backups by clicking the Restore Points tab on the top menu. You can administer both types of backups by clicking the Administer Backup Files icon on the Akeeba Backup Control Panel.

While you can download your backup files directly, it is strongly recommended you download them using an SFTP/FTP client to help prevent any corruption in the download process. Launch your favorite file transfer client and navigate to your backup directory. In this example, my backup directory is: /home/jpraterdemo/backups. All of your backups will use the .jpa file extension. Files are named based on the name of the profile, the domain name of your Joomla! website, and the date. Transfer your backups to another computer (like your home computer) for an extra layer of security.

Now that you have performed your first backup, it is time to learn how to restore a backup.

Performing a Restore

Up to this point, you have learned how to install, configure, and perform backups using Akeeba Backup. The last part of this tutorial will focus on restoring your Joomla! website to a new web host.

There are several ways to restore your Joomla! website using Akeeba?s products. Akeeba eXtract Wizard a program designed to unpack your archive files. This will give you complete access to your backed up files, directories, and databases. Using this wizard, you can then transfer your complete Joomla! website to another web host. The problem with this method is uploading lots of small files usually takes a long time. But do not worry, there is a faster way to restore your Joomla! website!

This is where Akeeba Kickstart comes into play. Akeeba Kickstart is a PHP script which unpacks backup archive online, so the only file you need to transfer to your new web host is simply your backup archive. Since transferring larger files is faster than transferring lots of small files, I recommend using this method to restore your Joomla! website.

Since this is for practice,  I would not recommend deleting your current website. You should create a test site to perform your restore to. Follow your web host?s instructions for creating a new website. This will give you the chance to practice performing a restore so when you do need to restore your Joomla! website, you will not be in such a panic to get it done correctly.

First, find the Akeeba Kickstart zip file you downloaded earlier. Unzip it, and look for these two files:

kickstart.php
xx-XX.kickstart.ini
Substitute xx-XX with the code for your language. Since I speak English, I used en-GB.kickstart.ini this example.

Upload these two files to the root web directory of your test website. In other words, you should be able to access Akeeba Kickstart by going to http://www.domain.com/kickstart.php. Next, upload your backup archive. Remember, this will have the .jpa file extension.

Now, open your web browser and go to http://www.domain.com/kickstart.php. As I mentioned in the previous section, Akeeba Backup bundles an installer within the backup archive. Kickstart is simply a web-based tool to unpack your backup--it does not actually restore your backup. But do not worry, you will perform a full restoration very soon!

Before you use Akeeba Kickstart, it provides some guidelines for its appropriate usage. Depending on your web host?s configuration, Akeeba Kickstart may not work at all. If this is the case, you will need to use Akeeba eXtract Wizard to unpack your backup archive, then manually SFTP/FTP the files to your web host. Press the Escape key to close this window and continue with the extraction.

Akeeba Kickstart will scan the directory it was uploaded to for Akeeba Backup archive files. All matching files will be listed in the Archive file drop-down box. In the Write to files section, select Directly. Depending on your web host?s configuration, you may need to change it to Use FTP instead. Keep the default execution times, then click the green Start button.

Depending on the size of your original Joomla! website, the restore could take anywhere from a few seconds to 10+ minutes. As with other Akeeba processes, do not close your browser or navigate to another page during the restore process. The progress bar will keep you updated with the status of the restore.

Once Kickstart has finished unpacking the backup archive, you can refresh your SFTP/FTP client to see your original files.

Click the Run the Installer button. This will launch the installer bundled by Akeeba Backup. You can also access the installer by going to http://www.domain.com/installation/index.php.

The Akeeba Backup Installer is very similar to the Joomla! installer. It will walk you through the same checks, configurations, and post-installation tasks. Make sure all the Required Settings are set to Yes. After verifying your new web host meets the default Joomla! requirements, click the Next button on the top-right.

You will now select your database type and enter the host and authentication information. Make sure you enter the database information for your new (or existing) web host. By default, Joomla! uses jos_ as a table prefix. Make sure this setting matches your previous setting or you  may experience compatibility problems with installed extensions. Once you have verified the database settings, click the Next button.

The restore process may take a seconds to a few minutes depending on the size of your database. Unlike Kickstart, this process is only restoring the Joomla! database. The only file that will be altered during the restore process is configuration.php in the root Joomla! directory.

Once the restore process completes successfully, click the OK button on the bottom-right of the window.

You can now enter site information like site name, email address, and URL. You can also configure the FTP options to optimize extension and media uploads. One option you should keep checked is Override tmp and log paths. This setting will set the path for your Joomla! /tmp directory and log files to match your new web host?s configuration. For example, if your previous site was located at /home/name/domain.com and your new site is located at /home/name/public_html, this setting will update the appropriate parameters in configuration.php.

Now your Joomla! website has been completely restored. Similar to the default Joomla! installation process, the /installation directory must be removed. The Akeeba Backup Installer can automate this for you. Click the remove the installation directory link to have this removed for you. Alternatively, you can SFTP/FTP into your web host to remove them.

Once the installation directory has been removed, a small window will open. Click the OK button on the bottom-left to be redirected to your Joomla! website?s homepage.

Congratulations! You have just performed a complete restore of your Joomla! website. Feel free to login to the Administrator panel to begin adding new content, installing new extensions, or, performing a new backup with Akeeba Backup!

Conclusion

Throughout this tutorial, you have learned how to install, configure, backup, and restore a Joomla! website using Akeeba Backup. While there are many ways to perform backups of your Joomla! website, none of them are as easy, efficient, and fool-proof as Akeeba Backup. As you have seen, you can easily install, backup, and restore your Joomla! website in a matter of minutes. There is no need to hassle with complicated commands or terminal windows! Everyone knows the importance of backing up their data on their personal computers and laptops, but how often do people treat their personal websites with the same kind of respect? You may not operate one of the top 500 blogs, but if something were to happen to your Joomla! website, what would you do? Would you be able to restore your previous blog posts and pictures? Now that you have been introduced to Akeeba Backup, there is no need to worry about a disaster because it puts you in total control of the backup and recovery of your website. Now, before you show another friend how to install Joomla!, make sure he knows how to back it up with Akeeba Backup!



blog comments powered by

Gazebo shed on saleDownload here
Read More ...

Steve Jobs` Era at Apple Ends

The official resignation by Jobs does not come as a complete surprise, as he had recently been plagued by various health problems.  Jobs turned over Apple?s reigns to Cook in January of this year when he was forced to take an indefinite medical leave.  It was his third such leave in recent years, but he was determined to return to his position, stating, ?I love Apple so much and hope to be back as soon as I can.?


Jobs? unfortunate medical history began in August of 2004, when he had surgery to treat a rare form of pancreatic cancer.  The procedure was a success, and Jobs returned to work the next month.  He ran into problems once again in January of 2009, when he claimed that his body?s ability to absorb proteins was being hindered due to a hormonal imbalance.  Jobs had liver transplant surgery in April of that year which kept him sidelined until July.


The string of health problems appear to have been his final undoing, as Jobs proclaimed that he was no longer able to perform as CEO in a letter to the Apple?s  board members as well as the company?s vast community.  He said, ?I have always said if there ever came a day when I could no longer meet my duties and expectations as Apple's CEO, I would be the first to let you know. Unfortunately, that day has come.? 


While his CEO days may be over, Jobs expressed the desire to continue contributing to the company as chairman of the board, director, and even as an employee.  He noted that such involvement would be subject to the board?s approval.  Jobs finished his letter on a positive note, stating, ?Apple's brightest and most innovative days are ahead of it.?  Jobs? last major public appearance as CEO of Apple occurred a couple of months ago during the Worldwide Developers Conference in June.  The conference was used as a springboard for the company?s introduction of its iCloud service and iOS5. 


Tim Cook?s career with Apple began in 1998.  Cook had served as Jobs? right-hand man for a lengthy period, and his duties included figurehead appearances at product launches, as well as leading several earning calls with investors and shareholders meetings.  The 50 year old assumed the position of chief operating officer in 2004.  Cook?s new role as CEO kicks off a new era for Apple, but the company?s booming success combined with his solid experience should make it a prosperous one. 


Apple recently held the distinction of being the world?s most valuable company thanks to its $330 billion-plus market capitalization.  Apple?s line of highly-noticeable products, including the Mac, iPhone, iPad, and iPod, has created an army of loyal followers and helped it separate itself from competitors.


 



blog comments powered by
Gazebo shed on saleDownload here
Read More ...

Ten Android Apps Dominate Smartphone Usage

Nielsen Smartphone Analytics collected its data during June of this year by installing meters on thousands of smartphones running on the Android operating system.  The meters tracked data concerning usage on the phones to give Nielsen researches further insight into how they are actually used.  The study found that the average Android user in the United States interacts with the web and uses apps for approximately 56 minutes per day.  Of the 56 minutes, 67 percent involves app usage, with the remaining third going to the web in the form of browsing and more.


A closer look into the app usage shows that only a very small portion of the Android Market?s catalog of over 250,000 apps is occupying users? time.   The 10 most popular Android apps account for 43 percent of app usage.  When you bump the analysis to include the top 50 Android apps, they account for 61 percent of overall usage.  In other words, the remaining 39 percent is left for the other 249,950-plus apps to fight over. 


It?s rather interesting that Nielsen did not release an actual listing of the top Android apps, but the company did say it plans to disclose the information during an upcoming webinar.  A little Google research into the topic shows that the Android Market?s top ten app list consists of YouTube, Facebook, Skype, Google Maps, Twitter, Adobe Reader/Flash, and Barcode Scanner.  One thing that definitely helps some of the top apps achieve such popularity is the fact a few come pre-installed on Android devices.  Their free status is helpful as well.  The top ten also consists of apps that perform basic, everyday functions that users want access to, such as communication and social networking.  When you think of a site like Facebook or a service like Skype, it?s not hard to figure out why people would spend so much time on them. 


Although Nielsen?s numbers show dominance by a small sector of the Android app world, the remaining market share is certainly worth fighting over.  Developers can certainly make a name for themselves by creating apps that are free or at least very affordable, plus offer a unique way to deliver functionality for everyday tasks.


For more on this topic, visit http://www.pcworld.com/article/238583/android_smartphones_dominated_by_10_apps.html



A Glimpse into the Future of HP?s WebOS


When Hewlett-Packard announced that it would stop the production of various webOS-based devices such as the TouchPad tablet and smartphones in the form of the Veer and Pre 3, many wondered what it meant for the future of the webOS platform itself.  HP purchased the mobile operating system from Palm for $1.2 billion last year, so it makes sense that the company?s executives stated a commitment to further development of the platform.  Stephen DeWitt, head of HP?s webOS division, ensured that webOS will continue to exist as far as PCs are concerned, and will also remain as the platform of choice for printers and other appliances.  In an interview with Engadget, he said: ?We made the decision to focus on the platform.  We could bring webOS to the market and expand the ecosystem...We can look at licensing; we can look at OEM and ODM-type relationships.?


Although a commitment does exist, there is still plenty of speculation of what is in store for webOS in the future.  To help clear things up a bit, a recent PCWorld article simplified the speculation to give a clearer glimpse into what developers and HP followers can look forward to from webOS.


Future Development


In terms of continued development of webOS in the near future, things appear to be looking up.  DeWitt confirmed HP?s commitment to the evolution, updating, and support of webOS in a recent statement to Bloomberg.  Speaking of updating, DeWitt also confirmed that updates for HP?s Veer and TouchPad devices would continue.  Whether or not that lasts well into the future remains to be seen.


HP App Catalog?s Prospects


HP?s interest in keeping app developers interested in webOS is still alive and well, at least if you take what Kerris recently wrote on the HP webOS Developer Blog to heart.  He noted: ?We will continue to support, innovate, and develop the webOS App Catalog. Our intent is to enhance our merchandising and presentation of your great products and to continue to build our webOS app ecosystem.?


While such a statement is promising, attracting developer attention to a platform lacking an upcoming product line is no easy task.  It becomes infinitely more difficult with competitors like Microsoft waiting in the wings.  The software giant knows it has a ripe opportunity to snatch up talented webOS developers, and it has begun doing its best to bring them over to Windows Phone 7?s side.  In a recent posting on Twitter, Brandon Watson, Microsoft?s director of developer experience for Windows Phone 7, offered webOS developers with published apps various incentives to develop for Microsoft?s mobile platform, including free development phones.  So far, the offer seems to be working, as it has piqued the interest of many webOS developers.


webOS? Expansion


For the time being, you can expect to see continued webOS development for PCs and printers, as HP proclaimed earlier this year that it would attempt to enhance Windows via webOS.  The enhancement would use the platform as a synchronization avenue between HP PCs and mobile webOS devices.


There also appears to be a chance that HP could expand webOS? reach into other devices besides phones, printers, and tablets.  DeWitt hinted at this when he told Bloomberg, ?There are going to be appliances of so many different sizes and shapes in the future that are going to require a human interface for data.?  In order to convert the expansion hopes into reality, HP is seeking solid webOS hardware partners.  ?HP webOS is an awesome software platform and now we can explore the best hardware partner for it,? wrote HP?s VP of worldwide developer relations for webOS Richard Kerris, on Twitter. 


blog comments powered by
Gazebo shed on saleDownload here
Read More ...

Wednesday, September 7, 2011

Using the IN and BETWEEN Operators on Tables

How to use the IN operator

Figure 3-13 shows how to code a WHERE clause that uses the IN operator. When you use this operator, the value of the test expression is compared with the list of expressions in the IN phrase. If the test expression is equal to one of the expressions in the list, the row is included in the query results. This is illustrated by the first example in this figure, which will return all rows whose terms_id column is equal to 1, 3, or 4.

You can also use the NOT operator with the IN phrase to test for a value that?s not in a list of expressions. This is illustrated by the second example in this figure. In this case, only those vendors who are not in California, Nevada, or Oregon are retrieved.

If you look at the syntax of the IN phrase shown at the top of this figure, you?ll see that you can code a subquery in place of a list of expressions. Subqueries are a powerful tool that you?ll learn about in chapter 6. For now, though, you should know that a subquery is simply a SELECT statement within another statement. In the third example in this figure, for instance, a subquery is used to return a list of vendor_id values for vendors who have invoices dated May 1, 2008. Then, the WHERE clause retrieves a vendor row only if the vendor is in that list. Note that for this to work, the subquery must return a single column, in this case, vendor_id.

The syntax of the WHERE clause with the IN operator

WHERE test_expression [NOT] IN ({subquery|expression_1 [, expression_2]...})

Examples of the IN operator

The IN operator with a list of numeric literals

WHERE terms_id IN (1, 3, 4)

The IN operator preceded by NOT

WHERE vendor_state NOT IN ('CA', 'NV', 'OR')

The IN operator with a subquery

WHERE vendor_id IN
(SELECT vendor_id
FROM invoices
WHERE invoice_date = '01-MAY-2008')

Description

You can use the IN operator to test whether an expression is equal to a value in a list of expressions. Each of the expressions in the list must evaluate to the same type of data as the test expression.
The list of expressions can be coded in any order without affecting the order of the rows in the result set.
You can use the NOT operator to test for an expression that?s not in the list of expressions.
You can also compare the test expression to the items in a list returned by a subquery as illustrated by the third example above. You?ll learn more about coding subqueries in chapter 6. --------------------------------------------Figure 3-13 How to use the IN operator


blog comments powered by

Gazebo shed on saleDownload here
Read More ...

Heroku Adds JCloud Platform Support, Java 7 News

Heroku first introduced its Celadon Cedar stack in May.  The company claimed that the stack would be able to run any language.  With the addition of Java to the mix, Heroku?s is solidified even further.  Adam Wiggins, co-founder of Heroku, described the latest move in a recent blog post, noting: ?Java is, by many measures, the world's most popular programming language.  In addition to its large and diverse developer base, it offers a huge ecosystem of libraries and tools, an extremely well-tuned VM for fast and reliable runtime performance, and an accessible C-like syntax.?


Bill Lapcevic, VP of business development at New Relic, claimed that Heroku?s support of Java would open new doors for the platform.  He said: ?New Relic and Heroku have been long-standing partners and it's great to see them expand their multi-language platform with support for Java.  This puts Heroku in a key position to serve Salesforce.com's developer audience as their platforms are largely Java- based. It also puts Heroku on the same playing field with existing solutions like Amazon Elastic Beanstalk. I expect there will be a significant uptake from enterprise developers looking for easy deployment and multi-language support for their critical apps."


Besides its popularity, Wiggins noted various other reasons for supporting Java.  He applauded the Java Virtual Machine (JVM) for its reliable memory footprint and impressive performance, calling it one of the best runtime VMs around.  Wiggins cited Java?s massive estimated population of six million developers as another reason to support the language, and said that it?s the industry?s best developed programming language for building server-side applications.  He cited the JVM runtime environment?s wide availability across platforms as an appealing factor as well.  ?Supporting Java is what's best for the large world of Java developers; it's what's best for developers who want to use other JVM languages; and it's even good for users of other languages, who will benefit indirectly from the learning their community may gain from contact with Java,? Wiggins said.


Wiggins also promised that Heroku would help to clean up certain problems associated with Java 2 Platform Enterprise Edition (J2EE), noting that the platform offers ?the capabilities promised by J2EE application containers for managing your app include deployment, restart, logging, service binding (config), and clustering (horizontal scaling.  Running your Java app on Heroku, you achieve these ends via the platform instead.?  He added, ?Using Heroku's platform to run Java apps finally solves the impedance mismatch between application containers designed for traditional software distribution, and the modern world of software-as-a-service.?


As for what to expect from Heroku in the future, Wiggins said, ?Future language packs will span the gamut from venerable (like Java) to cutting-edge (like Clojure and Node.js) to squarely in-between (like Ruby). Our desire is to be as inclusive as possible. Choice of language is up to the developer.?
For more on this topic, visit http://www.eweek.com/c/a/Application-Development/Heroku-to-Java-Welcome-to-Our-Cloud-832656/


A Glimpse into Java 7?s Improvements


The recent release of Java Platform Standard Edition 7 (Java SE 7) was the first major Java release in over five years.  It also marked the first Java introduction under the Oracle umbrella.  While Java SE 7 did have a bit of a rough start due to some serious bugs, it has been fixed, and it offers developers a host of new features that are worthy of praise.


Although Java SE 7 comes with various upgrades, some believe that its timing is its most important feature.  ?The main thing about Java SE is that it shipped. The inertia of five years without a release had to be overcome,? said Al Hilwa, an analyst at IDC.  Some features, such as language and VM support for modular programming and the ability to add closures for multi-core programming did not make the deadline for Java 7, meaning users will have to wait for Java 8?s release in 2012 to see them.


As for what?s available now, Java 7?s new and improved feature set begins with support for dynamic languages.  According to Hilwa, such support will promote the expansion of the Java ecosystem.  Thanks to a multi-core ready API, developers can decompose problems into tasks for parallel execution via multiple processor cores more efficiently.  Another Java 7 improvement serves to help increase developer productivity.  With Project Coin, coding is reduced and syntax is clarified through various language changes. 


Java founder James Gosling cited the new NIO2 file-system capabilities as one of his favorite features of Java 7.  NIO2 supplies a solid interface for working with file systems that offers detailed error information and broad access to file attributes.  Other Java 7 highlights of note include the new Gervill sound engine and the XRender pipeline for 2D graphics rendering.


blog comments powered by
Gazebo shed on saleDownload here
Read More ...

Clauses and Logical Operators for Retrieving Table Data

How to code the WHERE clause

The WHERE clause in a SELECT statement filters the rows in the base table so that only the rows you need are retrieved. In the topics that follow, you?ll learn a variety of ways to code this clause.

How to use the comparison operators

Figure 3-11 shows you how to use the comparison operators in the search condition of a WHERE clause. As you can see in the syntax summary at the top of this figure, you use a comparison operator to compare two expressions. If the result of the comparison is True, the row being tested is included in the query results.

The examples in this figure show how to use some of the comparison operators. The first WHERE clause, for example, uses the equal operator (=) to retrieve only those rows whose vendor_state column have a value of IA. Since the state code is a string literal, it must be enclosed in single quotes.

In contrast, a numeric literal like the one in the second WHERE clause isn?t enclosed in quotes. This clause uses the greater than (>) operator to retrieve only those rows that have a balance due greater than zero.

The third WHERE clause illustrates another way to retrieve all the invoices with a balance due. Like the second clause, it uses the greater than operator. Instead of comparing the balance due to a value of zero, however, it compares the invoice total to the total of the payments and credits that have been applied to the invoice.

The fourth WHERE clause illustrates how you can use comparison operators other than the equal operator with string data. In this example, the less than operator (<) is used to compare the value of the vendor_name column to a literal string that has the letter M in the first position. That will cause the query to return all vendors with names that begin with the letters A through L.

You can also use the comparison operators with date literals, as illustrated by the fifth and sixth WHERE clauses. The fifth clause will retrieve rows with invoice dates on or before May 31, 2008, and the sixth clause will retrieve rows with invoice dates on or after May 1, 2008. Like string literals, date literals must be enclosed in single quotes. In addition, you can use different formats to specify dates, as shown by the two date literals shown in this figure. You?ll learn more about the acceptable date formats and date comparisons in chapter 8.

The last WHERE clause shows how you can test for a not equal condition. To do that, you code a less than sign followed by a greater than sign. In this case, only rows with a credit total that?s not equal to zero will be retrieved.

Whenever possible, you should compare expressions that have similar data types. If you attempt to compare expressions that have different data types, Oracle may implicitly convert the data types for you. Although implicit conversions are often acceptable, they will occasionally yield unexpected results. In chapter 8, you?ll learn how to explicitly convert data types so your comparisons will always yield the results that you want.

The syntax of the WHERE clause with comparison operators

WHERE expression_1 operator expression_2
The comparison operators 




Examples of WHERE clauses that retrieve? 


Vendors located in Iowa 


[code]WHERE vendor_state = 'IA'

Invoices with a balance due (two variations)

WHERE invoice_total - payment_total - credit_total > 0
WHERE invoice_total > payment_total + credit_total

Vendors with names from A to L

WHERE vendor_name < 'M'

Invoices on or before a specified date

WHERE invoice_date <= '31-MAY-08'

Invoices on or after a specified date

WHERE invoice_date > = '01-MAY-08'

Invoices with credits that don?t equal zero

WHERE credit_total <> 0

Description

You can use a comparison operator to compare any two expressions that result in like data types. Although unlike data types may be converted to data types that can be compared, the comparison may produce unexpected results.
If the result of a comparison results in a True value, the row being tested is included in the result set. If it?s False or Unknown, the row isn?t included.
To use a string literal or a date literal in a comparison, enclose it in quotes. To use a numeric literal, enter the number without quotes.
Character comparisons are case-sensitive. ?CA? and ?Ca?, for example, are not equivalent. How to use the comparison operators


blog comments powered by

Gazebo shed on saleDownload here
Read More ...

PHP and the Law of Demeter

This might just be my opinion, but if I had to choose the flashiest star in the sky that is PHP, I'd have to mutter two words: Dependency Injection. Seated in solid logic, DI (or Inversion of Control) is the new kid on the block, which allows you to create modular, highly-testable classes that ask for their collaborators - either in their constructors or setters - instead of looking for them.

While I have to confess that I'm a big fan of DI, I admit that there are cases where it can be difficult to figure out what dependencies to inject into a given class. Further more, sometimes we're not careful enough when it comes to defining the responsibilities that classes will have in the context of an application, and end up passing them the wrong collaborators. Or - even worse - we pass in dependencies that are used internally as bridges or mediators for acquiring other objects, which makes classes "aware" of functionality that's completely unnecessary.

When this happens, it's a clear symptom of a common issue known as the "Law of Demeter" breakage. In case the name doesn't ring any bells, the "Law of Demeter" (http://en.wikipedia.org/wiki/Law_of_Demeter) - or the Principle of Least Knowledge - is a paradigm that allows to create loosely-coupled classes, based on a simple concept: each class should be designed to work properly using only the dependencies that it really needs.

This has more to do with common sense than any obscure programming principle. With a little willpower and planning, however, it's possible to create classes that adhere to the Law of Demeter.

I'll be demonstrating how the violation of this law can cause some undesired coupling effects in PHP classes, and how these artifacts can be fixed with minor hassle.

According to the formal definition excerpted from Wikipedia, a method of an object that doesn't invoke the following objects:

1) The object itself.
2) The method's parameters.
3) Any objects created/instantiated within the method. 4) The object's direct component objects. 5) A global variable, accessible by the object, in the scope of the method.

flagrantly infringes the Law of Demeter. While understanding the theoretical concepts is all well and fine, the best manner to understand the side effects of violating the law is by example. Keeping with this, I'm going to show you a use case where this occurs quite frequently: let's say that we need to implement a mapping layer that moves data between a domain model and a MySQL database, while keeping both isolated from one another.

The set of classes that compose the model are as follows:

(SampleApp/Model/AbstractEntity.php)


namespace SampleAppModel;
abstract class AbstractEntity
{
protected $_values = array();
protected $_allowedFields = array();

    /**
* Constructor
*/
public function __construct(array $fields)
{
foreach ($fields as $name => $value) {
$this->$name = $value;
}
}

    /**
* Assign a value to the specified field via the corresponding mutator (if it exists);
* otherwise, assign the value directly to the '$_values' array
*/
public function __set($name, $value)
{
if (!in_array($name, $this->_allowedFields)) {
throw new InvalidArgumentException("Setting the field '$name' is not allowed for this entity.");
}
$mutator = 'set' . ucfirst($name);
if (method_exists($this, $mutator) && is_callable(array($this, $mutator))) {
$this->$mutator($value);
}
else {
$this->_values[$name] = $value;
}
}

    /**
* Get the value of the specified field (via the getter if it exists or by getting it from the $_values array)
*/ 
public function __get($name)
{
if (!in_array($name, $this->_allowedFields)) {
throw new InvalidArgumentException("Getting the field '$name' is not allowed for this entity.");
}
$accessor = 'get' . ucfirst($name);
if (method_exists($this, $accessor) && is_callable(array($this, $accessor))) {
return $this->$accessor;
}
else if (isset($this->_values[$name])) {
return $this->_values[$name];
}
else {
throw new InvalidArgumentException("The field '$name' has not been set for this entity yet."); 
}
}

    /**
* Check if the specified field has been assigned to the entity
*/
public function __isset($name)
{
if (!in_array($name, $this->_allowedFields)) {
throw new InvalidArgumentException("The field '$name' is not allowed for this entity.");
}
return isset($this->_values[$name]);
}
/**
* Unset the specified field from the entity
*/
public function __unset($name)
{
if (!in_array($name, $this->_allowedFields)) {
throw new InvalidArgumentException("Unsetting the field '$name' is not allowed for this entity.");
}
if (isset($this->_values[$name])) {
unset($this->_values[$name]);
return true;
}
throw new InvalidArgumentException("The field '$name' has not been set for this entity yet.");
}

    /**
* Get an associative array with the values assigned to the fields of the entity
*/
public function toArray()
{
return $this->_values;
}    
}

(SampleApp/Model/User.php)


namespace SampleAppModel;
class User extends AbstractEntity
{  
protected $_allowedFields = array('id', 'name', 'email');

    /**
* Set the user's ID
*/
public function setId($id)
{
if(!filter_var($id, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 999999)))) {
throw new InvalidArgumentException('The ID of the user is invalid.');
}
$this->_values['id'] = $id;
}

    /**
* Set the user's full name 
*/ 
public function setName($name)
{
if (strlen($name)< 2 || strlen($name) > 32) {
throw new InvalidArgumentException('The name of the user is invalid.');
}
$this>_values['name'] = $name;
}

    /**
* Set the user's email address
*/ 
public function setEmail($email)
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('The email address of the user is invalid.');
}
$this->_values['email'] = $email;
}         
}

You may be familiar with the above classes, as Ive used them in some previous articles. In any case, all the earlier abstract parent does is define the structure and constraints of generic entities, while its subclass does something similar, only with users.

So far, these entity classes are pretty easy to grasp, so let's make the domain model a bit more functional by adding a class that handles the collections of domain objects. Here's the interface implemented by this collection, followed by its originating class:

(SampleApp/Model/Collection/CollectionInterface.php)


namespace SampleAppModelCollection;
use SampleAppModel;
interface CollectionInterface extends Countable, IteratorAggregate, ArrayAccess
{
public function toArray();
public function clear();
public function reset();
public function add($key, ModelAbstractEntity $entity);

    public function get($key);
public function remove($key);
public function exists($key);
}

(SampleApp/Model/Collection/EntityCollection.php)


namespace SampleAppModelCollection;
use SampleAppModel;
class EntityCollection implements CollectionInterface
{
protected $_entities = array();
/**
* Constructor
*/
public function  __construct(array $entities = array())
{
$this->_entities = $entities;
$this->reset();
}
/**
* Get the entities stored in the collection
*/
public function toArray()
{
return $this->_entities;
}
/**
* Clear the collection
*/
public function clear()
{
$this->_entities = array();
}

    /**
* Rewind the collection
*/     
public function reset()
{
reset($this->_entities);
}
/**
* Add an entity to the collection
*/
public function add($key, ModelAbstractEntity $entity) {
return $this->offsetSet($key, $entity);
}
/**
* Get from the collection the entity with the specified key
*/
public function get($key)
{
return $this->offsetGet($key);
}
/**
* Remove from the collection the entity with the specified key
*/
public function remove($key)
{
return $this->offsetUnset($key);
}
/**
* Check if the entity with the specified key exists in the collection
*/
public function exists($key)
{
return $this->offsetExists($key);
}
/**
* Count the number of entities in the collection
*/
public function count()
{
return count($this->_entities);
}
/**
* Get the external array iterator
*/
public function getIterator()
{
return new ArrayIterator($this->toArray());
}
/**
* Add an entity to the collection
*/ 
public function offsetSet($key, $entity)
{
if (!$entity instanceof ModelAbstractEntity) {
throw new InvalidArgumentException('The entity to be added to the collection must be an instance of EntityAbstract.');
}
if (!isset($key)) {
$this->_entities[] = $entity;
}
else {
$this->_entities[$key] = $entity;
}
return true;
}

    /**
* Remove an entity from the collection
*/
public function offsetUnset($key)
{
if ($key instanceof ModelAbstractEntity) {
$this->_entities = array_filter($this->_entities, function ($v) use ($key) {
return $v !== $key;
});
return true;
}
if (isset($this->_entities[$key])) {
unset($this->_entities[$key]);
return true;
}
return false;
}

    /**
* Get the specified entity in the collection
*/
public function offsetGet($key)
{
return isset($this->_entities[$key])
? $this->_entities[$key] : null;
}  

    /**
* Check if the specified entity exists in the collection
*/ 
public function offsetExists($key)
{
return isset($this->_entities[$key]);
}
}

As shown in the above code snippet, the "EntityCollection" class does exactly what its name suggests: it traverses, counts, and accesses a collection of entities by using an array-like notation (along with a few concrete methods). I assume that you must have coded a class like this several times before, so I'm not going to waste time explaining its inner workings.

Instead, let's recap what's been achieved so far: at this time there's a simple model domain, where user entities live in happy isolation, completely ignorant of any type of persistence mechanism that might exist out of their boundaries.

Although this is well and fine, there's still no single sign of a violation of the Law of Demeter. Well, be patient and let me show you the layer that will be responsible for accessing the underlying database and persisting the previous model.

Here are the elements that comprise this brand new tier:

(SampleApp/Library/Database/DatabaseAdapterInterface.php)


namespace SampleAppLibraryDatabase;
interface DatabaseAdapterInterface
{
function connect();
function disconnect();
function query($query);
function fetch();
function select($table, $conditions, $fields, $order, $limit, $offset);
function insert($table, array $data);
function update($table, array $data, $conditions);
function delete($table, $conditions);
function getInsertId();
function countRows();
function getAffectedRows();
}

(SampleApp\Library/Database/MysqlAdapter)


namespace SampleAppLibraryDatabase;
use SampleAppCommon;
class MysqlAdapter implements DatabaseAdapterInterface, CommonAbstractResource
{
protected $_config = array();
protected $_link;
protected $_result;

    /**
* Constructor
*/ 
public function __construct(array $config)
{
if (count($config) !== 4) {
throw new InvalidArgumentException('Invalid number of connection parameters.');
}
$this->_config = $config;
}

    /**
* Connect to MySQL
*/
public function connect()
{
// connect only once
if ($this->_link === null) {
list($host, $user, $password, $database) = $this->_config;
if (!$this->_link = @mysqli_connect($host, $user, $password, $database)) {
throw new RunTimeException('Error connecting to the server : ' . mysqli_connect_error());
}
unset($host, $user, $password, $database);
}
return $this->_link;

/**
* Execute the specified query
*/
public function query($query)
{
if (!is_string($query) || empty($query)) {
throw new InvalidArgumentException('The specified query is not valid.');
}
// lazy connect to MySQL
$this->connect();
if (!$this->_result = mysqli_query($this->_link, $query)) {
throw new RunTimeException('Error executing the specified query : ' . $query . mysqli_error($this->_link));
}
return $this->_result;
}

    /**
* Perform a SELECT statement
*/ 
public function select($table, $where = '', $fields = '*', $order = '', $limit = null, $offset = null)
{
$query = 'SELECT ' . $fields . ' FROM ' . $table
. (($where) ? ' WHERE ' . $where : '')
. (($limit) ? ' LIMIT ' . $limit : '')
. (($offset && $limit) ? ' OFFSET ' . $offset : '')
. (($order) ? ' ORDER BY ' . $order : '');
$this->query($query);
return $this->countRows();
}

    /**
* Perform an INSERT statement
*/  
public function insert($table, array $data)
{
$fields = implode(',', array_keys($data));
$values = implode(',', array_map(array($this, 'quoteValue'), array_values($data)));
$query = 'INSERT INTO ' . $table . ' (' . $fields . ') ' . ' VALUES (' . $values . ')';
$this->query($query);
return $this->getInsertId();
}

    /**
* Perform an UPDATE statement
*/
public function update($table, array $data, $where = '')
{
$set = array();
foreach ($data as $field => $value) {
$set[] = $field . '=' . $this->quoteValue($value);
}
$set = implode(',', $set);
$query = 'UPDATE ' . $table . ' SET ' . $set 
. (($where) ? ' WHERE ' . $where : '');
$this->query($query);
return $this->getAffectedRows();
}

    /**
* Perform a DELETE statement
*/
public function delete($table, $where = '')
{
$query = 'DELETE FROM ' . $table
. (($where) ? ' WHERE ' . $where : '');
$this->query($query);
return $this->getAffectedRows();
}

    /**
* Escape the specified value
*/ 
public function quoteValue($value)
{
$this->connect();
if ($value === null) {
$value = 'NULL';
}
else if (!is_numeric($value)) {
$value = "'" . mysqli_real_escape_string($this->_link, $value) . "'";
}
return $value;
}

    /**
* Fetch a single row from the current result set
*/
public function fetch($mode = MYSQLI_ASSOC)
{
if ($this->_result === null) {
return false; 
}
if (!in_array($mode, array(MYSQLI_NUM, MYSQLI_ASSOC, MYSQLI_BOTH))) {
$mode = MYSQLI_ASSOC;
}
if (($row = mysqli_fetch_array($this->_result, $mode)) === false) {
$this->freeResult();
}
return $row;
}

    /**
* Get the insertion ID
*/
public function getInsertId()
{
return $this->_link !== null
? mysqli_insert_id($this->_link) : null;
}

    /**
* Get the number of rows returned by the current result set
*/
public function countRows()
{
return $this->_result !== null
? mysqli_num_rows($this->_result) : 0;
}

    /**
* Get the number of affected rows
*/ 
public function getAffectedRows()
{
return $this->_link !== null
? mysqli_affected_rows($this->_link) : 0;
}

    /**
* Free up the current result set
*/ 
public function freeResult()
{
if ($this->_result === null) {
return false;
}
mysqli_free_result($this->_result);
return true;
}

    /**
* Close explicitly the database connection
*/ 
public function disconnect()
{
if ($this->_link === null) {
return false;
}
mysqli_close($this->_link);
$this->_link = null;
return true;
}

    /**
* Close automatically the database connection when the instance of the class is destroyed
*/
public function __destruct()
{
$this->disconnect();
}
}

As shown above, this data access layer is a no-brainer, as it's made up of a segregated interface and one single implementer, which turns out to be a MySQL abstraction class that executes a few common database operations, such as running queries, counting and retrieving table rows.

While the driving logic of the previous class (and its associated interface) is pretty easy to follow, it's valid to point out that it doesn't violate the Law of Demeter either. As I said before though, my goal here is to implement a set of data mappers that interconnect the earlier domain model with the persistence layer just defined.

To do so, I could take a more "relaxed" approach and build a service locator, which would be responsible for providing the mappers with their collaborators. It makes sense, doesn't it? Based on this idea, here're the interfaces that are implemented by the mentioned service locator, along with its spawning class:

(SampleApp/Common/AbstractResource.php)


namespace SampleAppCommon;
interface AbstractResource
{}

(SampleApp/Common/RegistrableInterface.php)


namespace SampleAppCommon;
interface RegistrableInterface
{
public function set($key, AbstractResource $resource);
public function get($key);
public function remove($key);
public function exists($key);
}

(SampleApp/Common/ServiceLocator.php)


namespace SampleAppCommon;
class ServiceLocator implements RegistrableInterface
{
protected $_resources = array();
/**
* Set the specified resource
*/
public function set($key, AbstractResource $resource)
{
if (!isset($this->_resources[$key])) {
$this->_resources[$key] = $resource;
}
return $this;
}

    /**
* Get the specified resource
*/
public function get($key)
{
if (isset($this->_resources[$key])) {
return $this->_resources[$key];
}
throw new InvalidArgumentException('The requested resource does not exist.');
}

    /**
* Remove the specified resource
*/
public function remove($key)
{
if (isset($this->_resources[$key])) {
unset($this->_resources[$key]);
return $this;
}
throw new InvalidArgumentException('The resource to be removed does not exist.');
}

    /**
* Check if the specified resource exists
*/
public function exists($key)
{
return isset($this->_resources[$key]);
}
}

The above "ServiceLocator" class implements a couple of trivial interfaces, allowing you to save and remove generic resources from an array-based registry. While the functionality of this locator seems to be acceptable when analyzed in isolation, it's relatively easy to take it to the next level. But the question that comes up here is: how can this be done?

Well, the class could be used for providing the aforementioned mappers with the dependencies that they need to work as intended. Sounds like a decent approach that would be implemented in a few easy steps, right?

As usual, the best manner to demonstrate this concept is with a concrete example. Thus, in the following section I'll be constructing the pertaining mappers, which will exploit the functionality offered by the earlier service locator to acquire their collaborators.

Moving Data Between the Data Access and Persistence Layers: Building a Set of Mappers

If you've come to this point in the article, it's because you're really interested in seeing what's so wrong with breaking the Law of Demeter. Again, the best way to demonstrate this is a concrete code sample, so be sure to check out the definition of the following data mappers, something that hopefully will make things clear for you:

(SampleApp/Model/Mapper/AbstractMapper.php)


namespace SampleAppModelMapper;
use SampleAppLibraryDatabase,
SampleAppModelCollection,
SampleAppModel,
SampleAppCommon;
abstract class AbstractMapper
{
protected $_adapter;
protected $_entityTable;
protected $_entityClass;
/**
* Constructor
*/
public function __construct(CommonServiceLocator $serviceLocator, array $entityOptions = array())
{
// Get the database adapter via the service locator
$this->_adapter = $serviceLocator->get('adapter');

   // Set the entity table if the options has been specified
if (isset($entityOptions['entityTable'])) {
$this->setEntityTable($entityOptions['entityTable']);
}

        // Set the entity class if the options has been specified
if (isset($entityOptions['entityClass'])) {
$this->setEntityClass($entityOptions['entityClass']);
}
// check if the entity options have been set
$this->;_checkEntityOptions();
}

    /**
* Check if the entity options have been set
*/
protected function _checkEntityOptions()
{
// check if the entity table has been set
if (!isset($this->_entityTable)) {
throw new InvalidArgumentException('The entity table has been not set yet.');
}
// check if the entity class has been set
if (!isset($this->_entityClass)) {
throw new InvalidArgumentException('The entity class has been not set yet.');
}
}
/**
* Get the database adapter
*/
public function getAdapter() 

return $this->_adapter; 
}
/** 
* Set the entity table 
*/ 
public function setEntityTable($entityTable)  

if (!is_string($entityTable) || empty($entityTable)) { 
throw new InvalidArgumentException("The given entity table '$entityTable' is invalid."); 

$this->_entityTable = $entityTable; 

/** 
* Get the entity table 
*/ 
public function getEntityTable() 

return $this->_entityTable; 

/** 
* Set the entity class 
*/ 
public function setEntityClass($entityClass) 

if (!is_subclass_of($entityClass, 'SampleAppModelAbstractEntity')) { 
throw new InvalidArgumentException("The given entity class '$entityClass' is invalid. It must be a subclass of AbstractEntity."); 

$this->_entityClass = $entityClass; 

/** 
* Get the entity class 
*/ 
public function getEntityClass() 

return $this->_entityClass; 

/** 
* Find an entity by its ID 
*/ 
public function findById($id) 

$this->_adapter->select($this->_entityTable, "id = $id"); 
if (!$data = $this->_adapter->fetch()) { 
return null; 

return new $this->_entityClass($data);         
}
/** 
* Find all the entities that match the specified criteria (or all when no criteria are given) 
*/ 
public function find($criteria = '') 

$collection = new CollectionEntityCollection; 
$this->_adapter->select($this->_entityTable, $criteria); 
while ($data = $this->_adapter->fetch()) { 
$collection[] = new $this->_entityClass($data); 

return $collection; 

/** 
* Insert a new row in the table corresponding to the specified entity 
*/ 
public function insert($entity) 
{  
if (!$entity instanceof $this->_entityClass) { 
throw new InvalidArgumentException("The entity to be inserted must be an instance of '$entityClass'."); 

return $this->_adapter->insert($this->_entityTable, $entity->toArray()); 
}
/** 
* Delete the row in the table corresponding to the specified entity or ID 
*/ 
public function delete($id) 

if ($id instanceof $this->_entityClass) { 
$id = $id->id; 

return $this->_adapter->delete($this->_entityTable, "id = $id"); 
}    
}

(SampleApp/Model/Mapper/UserMapper.php)


namespace SampleAppModelMapper;
class UserMapper extends AbstractMapper 

protected $_entityTable = 'users'; 
protected $_entityClass = 'SampleAppModelUser';   
}

At a glance, the mappers look pretty good, as the first one is an abstract parent that encapsulates the functionality required to perform CRUD operations in a MySQL database. This makes it possible to fetch one or multiple entities from storage, insert new domain objects, and delete existing ones as well. On the other hand, its subclass "User" does something similar, but specifically with user entities. Again, what's the big issue with these classes?

If you look closer at the parent's constructor, you'll realize that it takes an instance of the earlier service locator, which is used to get the database adapter. While admittedly this falls within the "while list" of the Law of Demeter, from a pragmatic standpoint, it's a clear violation of it. After all, why does the mapper have to use a mediator to obtain the adapter, when it can get it directly?

Even if the locator were saved as a protected/private property, this wouldn't make any difference. In both cases, the mapper is asking for the wrong collaborator, thus gaining access to functionality that it doesn't need at all. Not to mention that this approach has introduced a strong coupling, which makes testing the mappers a painful and inflexible process.

And if all of these reasons still don't convince you from the explosion of side effects generated by the breakage of the Law of Demeter, let me show you a short script, which uses all the classes developed previously to fetch a few users from the following MySQL table:

That's the table in question. Now, here's the mentioned script:


use SampleAppLibraryLoaderAutoloader as Autoloader, 
SampleAppCommonServiceLocator as ServiceLocator, 
SampleAppLibraryDatabaseMysqlAdapter as MysqlAdapter, 
SampleAppModelMapperUserMapper as UserMapper, 
SampleAppModelUser as User; 

// include the autoloader and create an instance of it 
require_once 'Library/Loader/Autoloader.php'; 
$autoloader = new Autoloader;


// create an instance of the service locator and store the MySQL adapter in it 
$serviceLocator = new ServiceLocator; 
$serviceLocator->set('adapter', new MysqlAdapter(array( 
'host',  
'user',  
'password',  
'database' 
)) 
);
// create an instance of the user mapper (the service locator is injected) 
$userMapper = new UserMapper($serviceLocator);

// fetch all users from the storage and display their data 
$users = $userMapper->find(); 
foreach ($users as $user) { 
echo ' ID: ' . $user->id  
. ' Name: ' . $user->name  
. ' Email: ' . $user->email . ''; 
}

// insert a new user into the storage 
$user = new User(array( 
'name'  => 'Margaret Dennis',  
'email' => 'margaret@domain.com' 
));
$userMapper->insert($user);
// delete an existing user from the storage 
$userMapper->delete(1);

If you run this script, (and assuming that you created the previous "users" table), it will nicely retrieve the corresponding domain objects, then insert a new user, and finally delete the first row from the corresponding table. To do so, however, first the database adapter needs to be saved in the service locator, and finally this one injected into the user mapper.

This approach not only requires you to write more lines of code, (which is the least of the damages), but it makes the whole API confusing, as it's not clear why the mapper needs the service locator to do its business. Of course, it's relatively easy to fix these issues and make the mapper to follow the commandments of the Law of Demeter. However, the solution will be implemented in the final part of this tutorial.

Final Thoughts

In this first post, I attempted to provide you with a humble introduction to what the Law of Demeter is and how its infringement can turn our PHP classes into heavily-coupled structures that are hard to test in isolation. As I said before, though, it's fairly simple to construct classes that stick to the law's requirements. Thus, if you're interested in seeing the fix up, don't miss the final part!



blog comments powered by

Gazebo shed on saleDownload here
Read More ...

An Overview of BlackBerry 7 OS

Productivity is one of BlackBerry 7?s strengths, as it should be.  RIM went ahead and made the Document to Go?s premium version free on all phones running BlackBerry 7.  The feature gives users the ability to view, edit, and compose files in Microsoft Word, Excel, and PowerPoint.  Such a move makes sense considering BlackBerry?s enterprise-friendly aura.

The BlackBerry Balance feature has also been added to the new OS, which helps to organize personal and business information separately when connected to a corporation?s BlackBerry Enterprise Server.  Security has not been ignored either.  With BlackBerry Protect, a user can now remotely locate a lost or stolen phone to lock it, wipe the data, or change the ringer volume.  BlackBerry Balance and Protect are both free.

While BlackBerry 7 does include improvements such as a digital compass, 720p HD video recording, and OpenGL ES 2.0 graphics support for 3D games, these additions only serve to help it catch up to its competitors.  Without a doubt, it?s a positive thing, but one has to wonder why RIM is so lethargic.  The company does deserve praise, however, for its inclusion of Near Field Communication (NFC) and augmented reality in BlackBerry 7.

NFC gives you the power to exchange information, such as a phone number or photo with another NFC-compatible phone.  It even allows you to make payments using your phone.  While the implementation of such a feature is great with the new OS, RIM is only making it available on the BlackBerry Bold 9900.  It?s still a step in the right direction at least.

Augmented reality makes an appearance in BlackBerry 7 via Wikitude, a browser that connects the real world with the virtual world through Flickr photos, Tweets, and Wikipedia data.  If you are looking for a comparison, think of Google Goggles.  This free download allows you to see nearby BBM friends by using your phone?s digital compass.  You can point the phone?s camera at a specific location, see if any BBM friends are close by, and chat with them.

The browser in BlackBerry 7 has received some attention from RIM, as the company added support for HTML5.  That?s a nice touch for video fans, but there is no support for Flash Player 10.  BlackBerry 6 was the setting of the introduction of a WebKit browser, which was definitely a welcome addition to the OS.  When you paired the WebKit status with things such as tabbed browsing, pinch-to-zoom multi-touch support, and more, you could see that RIM was on the right track.  This time around, the WebKit browser has been improved even more, especially in terms of speed.  RIM claims that pages load 40 percent faster in BlackBerry 7 than its OS predecessor, and the difference shows, especially on pages loaded with media. 

Universal search is one area where BlackBerry 7 is quite impressive.  The feature was introduced in BlackBerry 6, which put the platform on par with its competitors.  With Universal Search, you can scour your contacts, apps, and any other parts of your phone to find what you?re looking for.  You even have the option to adjust settings to certain categories to make the search much quicker.  Add in a voice command option new to BlackBerry 7, and you have a feature that can come in handy on a frequent basis.

When it comes to the home screen and apps drawer in BlackBerry 7 OS, the improvements are somewhat of a mixed bag.  Yes, the overall design of the home screen and apps is user-friendly and practical, but the look still leaves something to be desired.  In short, it still has a BlackBerry feel.  Of course, that is to be expected since you are using a BlackBerry after all, but when Android and iOS offer more modern aesthetics, you would think that RIM would finally get the hint.  You do get larger font that?s sharper, and the icons do give off a more vibrant and colorful aura, but the end result is still too similar to BlackBerry 6.

The applications drawer slides vertically, which is a plus.  The drawer is easy to customize so you can view apps in a way that meets your needs.  You can adjust the slider to view one or two rows of apps at a time, view all of your apps, or simply view none at all.  The option also exists to slide apps in a horizontal matter if you wish.  Adding an app to your Favorites is as simple as touching and holding its icon to reveal a menu filled with options.  App filtering is solid as well, with categories for frequent apps, favorite apps, downloads, and media.

Although BlackBerry 7 may not be the innovative release that many had hoped for, it is solid overall.  RIM equipped it with several features that at least helped the OS catch up to its competitors, as well as some extras in the form of augmented reality, NFC, and the Balance and Protect features.  One area where it does seem to be lacking is in appearance, so hopefully that will receive more attention the next time around.  Another thing to shoot for is legacy support, which is missing here.  In other words, if you own an older BlackBerry, even if it?s running on BlackBerry 6, you will not be able to upgrade to the latest version.

For more on this topic, visit http://www.pcworld.com/article/238123/blackberry_7_os_handson.html



blog comments powered by

Gazebo shed on saleDownload here
Read More ...

Tuesday, September 6, 2011

Retrieving Table Data with the LIKE Operator

How to use the LIKE operator

One final operator you can use in a search condition is the LIKE operator, shown in figure 3-15. You use this operator along with the wildcards shown at the top of this figure to specify the string pattern, or mask, that you want to match. The examples in this figure show how this works.

In the first example, the LIKE phrase specifies that all vendors in cities that start with the letters SAN should be included in the query results. Here, the percent sign (%) indicates that any characters can follow these three letters. So San Diego and Santa Ana are both included in the results.

The second example selects all vendors whose vendor name starts with the letters COMPU, followed by any one character, the letters ER, and any characters after that. Two vendor names that match that pattern are Compuserve and Computerworld.

The LIKE operator provides a powerful technique for finding information in a database that can?t be found using any other technique.

The syntax of the WHERE clause with the LIKE operator

WHERE match_expression [NOT] LIKE pattern

Wildcard symbols

Matches any string of zero or more characters.

WHERE clauses that use the LIKE operator

Results that match the mask the mask

WHERE vendor_name LIKE 'COMPU_ER%'

"Compuserve"and "Computerworld"

Description

You use the LIKE operator to retrieve rows that match a string pattern, called a mask. Within the mask, you can use special characters, called wildcard characters, that determine which values in the column satisfy the condition.
You can use the NOT operator before the LIKE operator. Then, only those rows with values that don?t match the string pattern will be included in the result set. --------------------------------------------Figure 3-15 How to use the LIKE operator


blog comments powered by

Gazebo shed on saleDownload here
Read More ...
Powered by Blogger.