Статьи

Идеальная работа — часть 1

Дзен и искусство найма

Это может показаться не таким уж сложным, но управление отделом кадров компании на самом деле является довольно сложной задачей. Конечно, парням и девчонкам не нужно беспокоиться об утечках памяти, сборке мусора или искаженном коде; однако их проблемы часто столь же неотразимы и трудны, как и проблемы любого программиста или дизайнера интерфейса.

Одной из самых сложных проблем для любого отдела кадров является подбор персонала — искусство сопоставления потенциальных сотрудников с вакансиями в организации. Процесс сложен: разместите рекламу, подождите, пока не появятся резюме, а затем приступите к трудной работе по их поиску потенциальных новобранцев. За этим обычно следуют собеседования и проверки рекомендаций потенциальных кандидатов и, возможно, предложение работы.

Теперь, еще до того, как Интернет стал модным словом, единственный способ сообщить потенциальным сотрудникам о работе — через рекламу, либо в газете, либо в журнале, либо по телевидению. И как только отклики начнут поступать, у HR будет незавидная задача пробираться сквозь кучи бумаги, чтобы найти идеальное соответствие для открытой позиции. По ходу дела вы почти всегда могли ожидать, что потеряете или уничтожите несколько таких резюме, перенесете несколько порезов бумагой и попрощаетесь с любой надеждой на социальную жизнь, которая у вас была.

Однако теперь, когда все и их глухие бабушки подключены к сети, связаться с потенциальными кандидатами стало намного проще. Теперь компания может либо рекламировать вакансии через онлайн-агентства по трудоустройству, либо размещать списки вакансий на своем веб-сайте. Так как агентства по трудоустройству онлайн не в восторге от своих сердец, последний вариант, как правило, тот, который предпочитают небольшие компании, хотя бы по экономическим причинам.

Конечно, создание рабочих мест на веб-сайте — это только верхушка айсберга. Как только эти резюме начинают поступать, все еще остается задача организовать их по категориям, перебрать все данные, чтобы составить короткий список кандидатов на собеседования, и подать отклоненные заявки на будущее. О да … и просто для того, чтобы сделать вещи интересными, обычно на заднем плане также тикают часы.

С помощью мощных инструментов с открытым исходным кодом, таких как PHP и MySQL, этот процесс может быть значительно упрощен. И в ходе этой статьи я собираюсь продемонстрировать, как, путем создания системы списков вакансий, подходящей для малого или среднего бизнеса.

Цель здесь двоякая: познакомить начинающих и промежуточных программистов с процессом разработки и реализации веб-приложения; и предложить менеджерам по персоналу и другим заинтересованным людям возможное решение их проблем.

Onwards!

Идеальный мир

Прежде чем приступить к изучению синтаксиса и структуры, важно понять проблемы, с которыми я сталкиваюсь, чтобы прояснить функциональные требования решения. Очень кратко, вот они:

  1. Каждое размещение звонка привлекает большое количество резюме. Большинство из этих резюме являются печатными документами, что затрудняет поиск среди них потенциальных кандидатов. В идеале резюме должны быть доступны в электронном виде.
  2. Резюме не в стандартном формате. Различные заявители используют разные шаблоны, стили и цвета, что затрудняет чтение и сортировку объема данных. В идеале каждое резюме должно быть в стандартном формате со стандартными полями и некоторой структурой, наложенной на данные внутри.
  3. С большим объемом ответов, заявления о приеме на работу могут оказаться неуместными или уничтоженными. В идеале, каждое резюме, введенное в систему, должно храниться там с любой потерей данных, чтобы к нему можно было получить доступ в любое время в будущем.
  4. Поиск в большом количестве заявлений о приеме на работу для определенных навыков или способностей занимает много времени. В идеале, база данных резюме должна легко найти поиск по заранее заданным критериям.

Поняв проблемы, становится легче определиться с требованиями решения. Анализ вышеуказанных проблем показывает, что большинство из них было бы решено, если бы у меня была система, которая:

  1. классифицировать резюме по работе;
  2. установил стандартную структуру содержания резюме;
  3. обработано резюме в электронном виде;
  4. встроенная поисковая система, позволяющая легко создавать подмножество базы данных, соответствующее определенным критериям;
  5. архивированные заявки в течение определенного периода времени;
  6. позволил администраторам легко добавлять и удалять списки вакансий с веб-сайта.

Следовательно, это составляет начальный набор функций для приложения и служит руководством для будущей деятельности по разработке.

Входная точка

На этом этапе я также выдвинул несколько идей относительно того, как эта система может работать.

Концептуально я могу считать эту систему разделенной на две части: пользовательский раздел и административный раздел. Вот первоначальный проект основного потока и правил, которые я планирую встроить в это приложение.

  1. В пользовательском разделе точкой входа для потенциального соискателя будет список вакансий, в котором будет отображен список открытых вакансий в организации. Это, в свою очередь, должно позволить заявителю выбрать конкретную работу и получить подробную информацию об обязанностях, квалификации, зарплате и другую основную информацию.
  2. Кандидат также будет иметь возможность заполнить форму заявки в режиме онлайн и предоставить организации личную информацию, квалификацию, опыт и другую информацию, обычно находящуюся в резюме. Эти данные будут храниться в базе данных, доступной и доступной для поиска администраторами.
  3. В разделе администрирования администраторы будут иметь возможность добавлять, редактировать и удалять списки вакансий.
  4. Раздел администрирования также будет содержать форму поиска, чтобы помочь администраторам искать сохраненные приложения для определенных навыков или квалификации.

Copyright Melonfire , 2000. Все права защищены.

Переход к базе данных

Записав требования, становится намного легче приступить к проектированию архитектуры системы. Первая (и самая важная) часть этого процесса проектирования — это проектирование базы данных, в которой я буду разрабатывать таблицы для хранения данных приложения.

Сейчас самое время загрузить исходный код , чтобы вы могли ссылаться на него в этой статье (вам понадобится веб-сервер, поддерживающий PHP и базу данных MySQL).

Я планирую хранить списки вакансий в одной таблице списков.

#  # Table structure for table 'listing'  #   DROP TABLE IF EXISTS listing;  CREATE TABLE listing (  jcode varchar(10) NOT NULL,  designation varchar(255) NOT NULL,  responsibilities text NOT NULL,  qualifications text NOT NULL,  cname varchar(255) NOT NULL,  cmail varchar(255) NOT NULL,  posted date DEFAULT '0000-00-00' NOT NULL,  fk_department tinyint(3) unsigned DEFAULT '0' NOT NULL,  fk_location tinyint(3) unsigned DEFAULT '0' NOT NULL,  fk_salary tinyint(3) unsigned DEFAULT '0' NOT NULL,  PRIMARY KEY (jcode),  KEY jcode (jcode)  );   #  # jcode - unique identifier for each job listing  # designation - job designation  # responsibilities - job responsibilities  # qualifications - job qualifications  # cname - name of person posting job  # cmail - email address of person posting job  # fk_department - foreign key - which department is this job in? #  fk_location - foreign key - which city is this job in? (for  multi-location organizations) # fk_salary - foreign key - what is the  expected compensation range for this job?  # 

Давайте заполним это парочкой фиктивных записей.

 #  # Dumping data for table 'listing'  #   INSERT INTO listing (jcode, designation, responsibilities,  qualifications, cname, cmail, posted, fk_department, fk_location,  fk_salary) VALUES ( 'X5436', 'Senior Web Developer', 'Applicant will  be responsible for developing Web applications and executing  Web-related projects for corporate customers. ', 'Applicant should be  familiar with scripting languages (PHP and Perl), databases (mySQL,  PostgreSQL). Applicant should be comfortable with both Windows and  *NIX operating system. Applicant will also be required to demonstrate  a thorough knowledge of software design and engineering principles.',  'Roger Rabbit', '[email protected]', '2001-05-22', '3', '4', '1');  INSERT INTO listing (jcode, designation, responsibilities, qualifications,  cname, cmail, posted, fk_department, fk_location, fk_salary) VALUES (  'KA6547', 'Project Manager', 'Applicant will be responsible for managing  projects within the organization. Responsibilities include developing  project plans and schedules, tracking project progress, communicating with  the customer, and ensuring that deadlines and deliveries are met.',  'Applicant should be familiar with office applications like Word, Excel,  Powerpoint and Project. Applicant should have prior experience  with project management tasks, and must bring enthusiasm  and professionalism to the post.', 'Bugs Bunny',  '[email protected]', '2001-04-05', '4', '5', '11'); 

Как вы можете видеть, эта таблица ссылается на информацию в трех других таблицах с помощью внешних ключей — давайте рассмотрим их далее, а также примеры записей для каждой из них.

 #  # Table structure for table 'location'  #   DROP TABLE IF EXISTS location;  CREATE TABLE location (   id tinyint(3) unsigned NOT NULL auto_increment,   location varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # location - name of city where applicant will be posted  #   # Dumping data for table 'location'  #   INSERT INTO location (id, location) VALUES ( '1', 'New York'); INSERT  INTO location (id, location) VALUES ( '2', 'London'); INSERT INTO  location (id, location) VALUES ( '3', 'Paris'); INSERT INTO location  (id, location) VALUES ( '4', 'Tokyo'); INSERT INTO location (id,  location) VALUES ( '5', 'Bombay');   #  # Table structure for table 'department'  #   DROP TABLE IF EXISTS department;  CREATE TABLE department (   id tinyint(3) unsigned NOT NULL auto_increment,   department varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # department - name of department  #   # Dumping data for table 'department'  #  INSERT INTO department (id, department) VALUES ( '1', 'Human  Resources'); INSERT INTO department (id, department) VALUES ( '2',  'Accounting'); INSERT INTO department (id, department) VALUES ( '3',  'Engineering'); INSERT INTO department (id, department) VALUES ( '4',  'Design'); INSERT INTO department (id, department) VALUES ( '5',  'Administration');   #  # Table structure for table 'salary'  #   DROP TABLE IF EXISTS salary;  CREATE TABLE salary (   id tinyint(3) unsigned NOT NULL auto_increment,   salary varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # salary - salary range  #   #  # Dumping data for table 'salary'   #  INSERT INTO salary (id, salary) VALUES ( '1', 'Not specified'); INSERT  INTO salary (id, salary) VALUES ( '2', '< USD 20,000'); INSERT INTO  salary (id, salary) VALUES ( '3', 'USD 20,000-29,900'); INSERT INTO  salary (id, salary) VALUES ( '4', 'USD 30,000-39,900'); INSERT INTO  salary (id, salary) VALUES ( '5', 'USD 40,000-49,900'); INSERT INTO  salary (id, salary) VALUES ( '6', 'USD 50,000-59,900'); INSERT INTO  salary (id, salary) VALUES ( '7', 'USD 60,000-69,900'); INSERT INTO  salary (id, salary) VALUES ( '8', 'USD 70,000-79,900'); INSERT INTO  salary (id, salary) VALUES ( '9', 'USD 80,000-89,900'); INSERT INTO  salary (id, salary) VALUES ( '10', 'USD 90,000-99,900'); INSERT INTO  salary (id, salary) VALUES ( '11', '> USD 100,000'); 

Это очень простые таблицы, каждая из которых имеет первичный ключ (id) и соответствующее значение. В случае, если вам интересно, почему я разбил эти элементы на отдельные таблицы, а не включил их все в таблицу «перечисления» или даже жестко запрограммировал их в конечном приложении, причина очень проста: я хочу сделать администратору проще добавлять и редактировать эти значения.

Разбивая их на отдельные таблицы, администратор, который хочет настроить приложение (например, изменить номер и название местоположения работы или отредактировать названия различных отделов), может сделать это без необходимости возиться с кодом программы. Это часть процесса, известного как «нормализация», и это очень важно при проектировании базы данных с двумя или более таблицами (ссылки на некоторые хорошие статьи по нормализации приведены в конце этой статьи).

Copyright Melonfire , 2000. Все права защищены.

Пять рупий

Четыре таблицы, которые вы видели выше, позволят мне построить страницы, на которых нужно перечислить открытые вакансии вместе с их описаниями. Однако, как только пользователь решил подать заявку на работу, данные, которые он вводит, должны где-то храниться. Следовательно, я создал несколько таблиц для хранения этой информации.

Обратите внимание, что информацию, которую я планирую запросить в форме заявления о приеме на работу, можно в целом разбить на пять разделов (личная информация, образование, история работы, навыки и ссылки), и поэтому таблицы, которые я построил, также соответствуют этой разбивке.

 #  # Table structure for table 'r_user'    DROP TABLE IF EXISTS r_user;  CREATE TABLE r_user (   rid tinyint(3) unsigned NOT NULL auto_increment,   jcode varchar(10) NOT NULL,   fname varchar(255) NOT NULL,   lname varchar(255) NOT NULL,   dob date DEFAULT '0000-00-00' NOT NULL,   addr1 varchar(255) NOT NULL,   addr2 varchar(255),   city varchar(255) NOT NULL,   state varchar(255) NOT NULL,   zip varchar(10) NOT NULL,   fk_country tinyint(3) unsigned DEFAULT '0' NOT NULL,   phone varchar(25) NOT NULL,   email varchar(255) NOT NULL,   url varchar(255),   relo tinyint(4) DEFAULT '0' NOT NULL,   posted date DEFAULT '0000-00-00' NOT NULL,   PRIMARY KEY (rid),   KEY jcode (jcode),   KEY rid (rid)  );   #  # rid - unique identifier for each application/resume, used to reference  it throughout the application  # jcode - job this application is for  # fname - applicant's first name  # lname - applicant's last name  # dob - applicant's date of birth  # addr1 - applicant's address  # add2 - applicant's address  # city - applicant's city  # state- applicant's state  # zip - applicant's zip code  # fk_country - applicant's country; foreign key to "country" table  # phone - applicant's phone number  # email - applicant's email address  # url - applicant's Web site  # relo - whether applicant is willing to relocate  # posted - date application was posted  # 

Таблица « r_user » содержит личную информацию заявителя и содержит одну запись на приложение; поле » rid » служит уникальным идентификатором для каждого приложения.

 #  # Table structure for table 'r_education'  #   DROP TABLE IF EXISTS r_education;  CREATE TABLE r_education (   rid tinyint(3) unsigned DEFAULT '0' NOT NULL,   institute varchar(255) NOT NULL,   fk_degree tinyint(3) unsigned DEFAULT '0' NOT NULL,   fk_subject tinyint(3) unsigned DEFAULT '0' NOT NULL,   year year(4) DEFAULT '0000' NOT NULL,   KEY fk_degree (fk_degree),   KEY fk_subject (fk_subject),   KEY rid (rid)  );   #  # rid - which application is this information for?  # institute - name of educational institution  # fk_institute - degree type; foreign key to "degree" table  # fk_subject - degree subject; foreign key to "subject" table # year -  degree obtained in which year? #   #  # Table structure for table 'r_employment'  #   DROP TABLE IF EXISTS r_employment;  CREATE TABLE r_employment (   rid tinyint(3) unsigned DEFAULT '0' NOT NULL,   employer varchar(255) NOT NULL,   fk_industry tinyint(3) unsigned DEFAULT '0' NOT NULL,   start_year year(4) DEFAULT '0000' NOT NULL,   end_year year(4) DEFAULT '0000' NOT NULL,   responsibilities text NOT NULL,   KEY rid (rid)  );   #  # rid - which application is this information for?  # employer - name of employer  # fk_industry - employer's industry; foreign key to "industry" table #  start_year - started work in...? # end_year - ended work in...?  # responsibilities - free-form description of job  responsibilities at this workplace  #   #  # Table structure for table 'r_skill'  #   DROP TABLE IF EXISTS r_skill;  CREATE TABLE r_skill (   rid tinyint(3) unsigned DEFAULT '0' NOT NULL,   skill varchar(255) NOT NULL,   experience tinyint(3) unsigned DEFAULT '0' NOT NULL,   KEY skill (skill),   KEY experience (experience),   KEY rid (rid)  );   #  # rid - which application is this information for?  # skill - name of skill  # experience - years experience in this skill  #   #  # Table structure for table 'r_reference'  #   DROP TABLE IF EXISTS r_reference;  CREATE TABLE r_reference (   rid tinyint(3) unsigned DEFAULT '0' NOT NULL,   name varchar(255) NOT NULL,   phone varchar(25) NOT NULL,   email varchar(255),   KEY rid (rid)  );   #  # rid - which application is this information for?  # name - reference's name  # phone - reference's phone number  # email - reference's email address  # 

r_education « r_education », « r_employer », « r_skills » и « r_reference » содержат образование, историю работы, навыки и ссылки соответственно. Обратите внимание, что эти таблицы могут содержать более одной записи для каждого кандидата (поскольку кандидат может перечислить несколько навыков или ссылок в одном приложении), причем записи связаны друг с другом через уникальное поле « rid ».

Copyright Melonfire , 2000. Все права защищены.

Счастливчик 13

Наконец, у меня есть несколько вспомогательных таблиц, в которых хранятся списки стран, отраслевые списки, тематические списки и тому подобное. Опять же, они были помещены в отдельные таблицы и связаны с помощью внешних ключей, чтобы упростить настройку приложения для различных нужд. Вот они:

 #  # Table structure for table 'country'  #   DROP TABLE IF EXISTS country;  CREATE TABLE country (   id tinyint(4) unsigned NOT NULL auto_increment,   country varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # country - country name  #   #  # Dumping data for table 'country'  #   INSERT INTO country (id, country) VALUES ( '', 'Afghanistan'); INSERT  INTO country (id, country) VALUES ( '', 'Albania');   #  # Table structure for table 'degree'  #   DROP TABLE IF EXISTS degree;  CREATE TABLE degree (   id tinyint(3) unsigned NOT NULL auto_increment,   degree varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # degree - degree type  #   #  # Dumping data for table 'degree'  #   INSERT INTO degree (id, degree) VALUES ( '1', 'High School degree');  INSERT INTO degree (id, degree) VALUES ( '2', 'Undergraduate degree');  INSERT INTO degree (id, degree) VALUES ( '3', 'Bachelor's degree');   #  # Table structure for table 'industry'  #    DROP TABLE IF EXISTS industry;  CREATE TABLE industry (   id tinyint(4) unsigned NOT NULL auto_increment,   industry varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # industry - industry type  #   #  # Dumping data for table 'industry'  #   INSERT INTO industry (id, industry) VALUES ( '1', 'Advertising');  INSERT INTO industry (id, industry) VALUES ( '2', 'Agriculture and  Forestry');   #  # Table structure for table 'subject'  #   DROP TABLE IF EXISTS subject;  CREATE TABLE subject (   id tinyint(3) unsigned NOT NULL auto_increment,   subject varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # subject - subject name  #    #  # Dumping data for table 'subject'  #   INSERT INTO subject (id, subject) VALUES ( '', 'Accounting'); INSERT  INTO subject (id, subject) VALUES ( '', 'Actuarial Science'); 

Файл «jobs.sql» в архиве исходного кода содержит более длинный список элементов для этих вспомогательных таблиц.

Сколько это столов? Ну, Джо, это счастливый номер тринадцать! Гулянка!

Copyright Melonfire , 2000. Все права защищены.

Строим фундамент

С разработанной базой данных, пришло время написать код для взаимодействия с ней. Первый скрипт, job_list.php, является точкой входа в приложение и просто отображает список доступных заданий, классифицированных по отделам.

 <?  // job_list.php - display list of open jobs   // includes  include("config.php");  include("functions.php");  ?>   <html>  <head>  <basefont face="Verdana" size="2">  </head>   <body bgcolor=white>   <? $image="listings.jpg"; ?>  <? include("header.inc.php"); ?>   <?  // generate list  ?>   <? include("footer.inc.php"); ?>   </body>  </html> 

Каждая страница, созданная с помощью этого приложения, имеет определенный макет — синий баннер вверху, логотип справа и заголовок (на самом деле изображение). Внизу каждой страницы есть уведомление об авторских правах и отказ от ответственности. Поскольку эти элементы останутся постоянными, через приложение я поместил соответствующий HTML-код в отдельные файлы верхнего и нижнего колонтитула и просто include() d их на каждой странице.

Опять же, разделив общие элементы интерфейса на отдельные файлы, я упростил настройку внешнего вида приложения; просто измените эти файлы, и изменения будут отражены на всех страницах.

Переменная $image хранит имя заголовка изображения для каждой страницы и используется «header.inc.php» — как вы можете видеть.

 <!-- appears at the top of every page -->  <table bgcolor="6583C3" width="100%" cellspacing="0" cellpadding="0">  <tr> <td height=50 align=right>&nbsp;<img src="images/header.gif"  alt="" border="0"></td>  </tr>  </table>  <p>  <img src="images/<? echo $image; ?>" alt="" border="0">  <p>  <!-- end of header.inc --> 

Кроме того, два других файла, «config.php» и «functions.php», также включены вверху каждой страницы — они хранят информацию о доступе к базе данных и полезные функции, соответственно.

 <?  // config.php - useful variables   // database parameters  // alter this as per your configuration  $database="jobs";  $user = "root";  $pass = "";  $hostname = "localhost";   ?> 

Вернуться к «job_list.php» — вот код, который заботится о подключении к базе данных и фактически генерирует список вакансий.

 <? include("header.inc.php"); ?>   <?   // open connection to database  $connection = mysql_connect($hostname, $user, $pass) or die ("Unable  to connect!");   // get list of departments with open jobs  $query = "SELECT DISTINCT id, department from department, listing  WHERE department.id = listing.fk_department"; $result =  mysql_db_query($database, $query, $connection) or die ("Error in  query: $query. " . mysql_error());   // iterate through resultset  while(list($id, $department) = mysql_fetch_row($result))  {   // print department name  echo "<b>Department:</b> $department";   // look for jobs within the department and print as list  $query2 = "SELECT jcode, designation from listing WHERE  listing.fk_department = '$id'";  $result2 = mysql_db_query($database, $query2, $connection) or die  ("Error in query: $query2. " . mysql_error());   echo "<ul>";  while(list($jcode, $dsg) = mysql_fetch_row($result2))  {  echo "<li><a href=job_details.php?jcode=$jcode>$dsg  ($jcode)</a>";  }  echo "</ul>";   echo "<p>";  }   // clean up  mysql_close($connection);  ?>   <? include("footer.inc.php"); ?> 

В этом случае я сначала запросил таблицу «листинга» для списка отделов, в которых есть открытые вакансии — обратите внимание на ключевое слово DISTINCT чтобы исключить дублирующиеся записи. Затем для каждого из этих отделов я распечатал обозначение должности и код задания и связал его со сценарием, который будет отображать дополнительную информацию.

Вот как это выглядит:

867image10

Copyright Melonfire , 2000. Все права защищены.

Дьявол кроется в деталях

Сценарий «job_details.php» предназначен для принятия конкретного кода работы, подключения к базе данных и печати таких сведений, как квалификация и обязанности для этой работы. Он также содержит ссылку на форму заявления о приеме на работу, если пользователь заинтересован в подаче заявления на работу.

 <?  // job_details.php - display job details   // includes   // check for missing parameters  if (!$jcode || $jcode == "")  {  header("Location:error.php");  exit;  }   // open connection to database  $connection = mysql_connect($hostname, $user, $pass) or die ("Unable  to connect!");   // get job details  // use a join to get data from different tables  $query = "SELECT listing.designation, listing.jcode,  department.department, location.location, salary.salary,  listing.responsibilities, listing.qualifications, listing.cname,  listing.cmail, listing.posted from department, listing, location,  salary WHERE department.id = listing.fk_department AND location.id =  listing.fk_location AND salary.id =  listing.fk_salary AND listing.jcode = '$jcode'";  $result = mysql_db_query($database, $query, $connection) or die ("Error in  query: $query. " . mysql_error());   // error check  if (mysql_num_rows($result) <= 0)  {  header("Location:error.php");  exit;  }  else  {  // obtain data from resultset  list($designation, $jcode, $department, $location, $salary,  $description, $qualification, $cname, $cmail, $posted) =  mysql_fetch_row($result);   // clean up  mysql_close($connection);  ?>   <!-- snip -->   <!-- print job details -->  <b>Designation:</b> <? echo $designation; ?>  <p>  <b>Department:</b> <? echo $department; ?>  <p>   <!-- snip -->    <b>Posted on:</b> <? echo fixDate($posted); ?>  <p>  <!-- link to application form -->  <a href="apply.php?jcode=<? echo $jcode; ?>">Apply online</a> for this  job, or <a href="job_list.php">return to job listings</a>   <!-- snip -->   <?  }  ?> 

Первое, что делает этот скрипт, это проверяет, что ему был передан код задания, через метод URL GET . Если этот код задания отсутствует, управление передается универсальному обработчику ошибок через перенаправление HTTP и функцию header() .

Предполагая, что код задания присутствует, следующее, что нужно сделать, — убедиться, что он действителен и что такое задание существует в базе данных. Запрос выполняется для получения полного описания задания (путем соединения таблицы «листинга» с другими вспомогательными таблицами через внешние ключи). Если запрос возвращает значение, информация печатается; если нет, обработчик ошибок вызывается снова.

Функция list() используется для разделения различных элементов возвращаемой строки и назначения их обычным переменным; затем они печатаются в соответствующих местах. В конце ссылка на скрипт «apply.php» переводит пользователя в форму заявки, снова используя код задания в качестве идентификатора.

Обратите внимание на fixDate() — она ​​используется для превращения типа DATE в MySQL во что-то более читаемое и читается из «functions.php».

 <?  // function to format mySQL DATE values  function fixDate($val)  {  //split it up into components  $arr = explode(" ", $val);  $datearr = explode("-", $arr[0]);  // create a timestamp with mktime(), format it with date() return  date("d MY", mktime(0, 0, 0, $datearr[1], $datearr[2], $datearr[0]));  }  ?> 

Вот как это выглядит:

867image20

Copyright Melonfire , 2000. Все права защищены.

Применяя себя

Форма заявления о работе «apply.php» и соответствующий обработчик данных «apply_rslt.php» составляют ядро ​​этого приложения. Они отвечают за генерацию необработанных данных и их надлежащее хранение в базе данных; следовательно, особое внимание должно быть уделено их разработке.

Как и предыдущий скрипт, «apply.php» должен сначала проверить, чтобы убедиться, что он получил действительный код задания.

 <?  // apply.php - generate application form   // includes   // error checks   // open connection to database  $connection = mysql_connect($hostname, $user, $pass) or die ("Unable  to connect!");   // get job details  // use a join to get data from different tables  $query = "SELECT designation, jcode, department from listing,  department WHERE jcode = '$jcode' AND department.id =  listing.fk_department"; $result = mysql_db_query($database, $query,  $connection) or die ("Error in  query: $query. " . mysql_error());   // error check  if (mysql_num_rows($result) <= 0)  {  header("Location:error.php");  exit;  }  else  {  // obtain data from resultset  list($designation, $jcode, $department) = mysql_fetch_row($result);   mysql_free_result($result);  ?> 

Остальная часть сценария является обычной формой; он включает в себя как текстовые поля, так и раскрывающиеся списки выбора, многие из которых генерируются из вспомогательных таблиц, обсуждавшихся ранее. Взгляните на раздел личной информации.

  <table border="0" cellspacing="5" cellpadding="2">  <form action="apply_rslt.php" method="post">  <input type="hidden" name="jcode" value="<? echo $jcode; ?>"   <!-- personal information section -->   <tr>  <td colspan=4><img src="images/pi.gif"></td>  </tr>   <tr>  <td colspan=2>First name<font color="red">*</font></td>  <td colspan=2>Last name<font color="red">*</font></td>  </tr>   <tr>  <td colspan=2><input type="text" name="fname" size="20"  maxlength="255"></td> <td colspan=2><input type="text" name="lname"  size="20" maxlength="255"></td>  </tr>   <!-- snip -->   <tr>  <td colspan=4>Country<font color="red">*</font></td>  </tr>   <tr>  <td colspan=4><select name="country">  <?  // get country list  $query = "SELECT id, country from country";  $result = mysql_db_query($database, $query, $connection) or die  ("Error in  query: $query. " . mysql_error());  while (list($id, $country) = mysql_fetch_row($result))  {  echo "<option value=$id>$country</option>";  }  mysql_free_result($result);  ?>  </select></td>  </tr>   <!-- snip -->   <tr>  <td colspan=4>Date of birth<font color="red">*</font><br><font  size="-2">(in dd-mm-yyyy format)</font></td> </tr>   <tr>  <td colspan=4>  <select name="dd">  <? for ($x=1; $x<=31; $x++) { echo "<option value="" .  sprintf("%02d", $x) . "">" . sprintf("%02d", $x) . "</option>";  } ?>  </select> -  <select name="mm">  <? for ($x=1; $x<=12; $x++) { echo "<option value="" .  sprintf("%02d", $x)  . "">" . sprintf("%02d", $x) . "</option>";  } ?>  </select> -  <select name="yyyy">  <!-- display from 1940 to (current year-10) -->  <? for ($x=1940; $x<=(date("Y", mktime())-10); $x++) { echo "<option  value=$x>$x</option>"; } ?>  </select>  </td>  </tr> 

Как вы можете видеть, список выбора страны генерируется из таблицы «страна» в базе данных, а раскрывающиеся списки даты рождения создаются с помощью циклов «for».

Раздел «Образование» использует таблицы «степень» и «предмет» для составления списка возможных образовательных квалификаций.

  <!-- education section -->  <tr>  <td colspan=4><img src="images/ed.gif"></td>  </tr>   <tr>  <td colspan=4><i>You may fill all or none of the rows below; ensure  that no fields are left empty per filled-in row</i></td>  </tr>   <tr>  <td>Institute/University<br><font size=-2>(example: XYZ  University)</td> <td>Degree<br><font size=-2>(example: Master's  degree)</td> <td>Primary subject<br><font size=-2>(example:  Accounting)</td> <td>Year<br><font size=-2>(example: 1992)</td> </tr>   <?  // get degree list  $query = "SELECT id, degree from degree";  $degree_result = mysql_db_query($database, $query, $connection) or die  ("Error in query: $query. " . mysql_error());   // get subject list  $query = "SELECT id, subject from subject";  $subject_result = mysql_db_query($database, $query, $connection) or  die ("Error in query: $query. " . mysql_error());   for ($x=0; $x<5; $x++)  {  ?>  <tr>  <td><input type="text" name="institute[]" size="20"  maxlength="255"></td> <td><select name="degree[]"> <?  while (list($id, $degree) = mysql_fetch_row($degree_result))  {  echo "<option value=$id>$degree</option>";  }  // same data, no need to query again  mysql_data_seek($degree_result, 0);  ?>  </select></td>  <td><select name="subject[]">  <?   while (list($id, $subject) = mysql_fetch_row($subject_result))  {  echo "<option value=$id>$subject</option>";  }  // same data, no need to query again mysql_data_seek($subject_result,  0); ?>  </select></td>  <td><input type="text" name="degree_year[]" size="4" maxlength="4"></td>  </tr>   <?  }  mysql_free_result($degree_result);  mysql_free_result($subject_result);  ?> 

Как видите, приведенный выше код сгенерирует пять строк, чтобы кандидат мог ввести до пяти различных квалификаций. Вы заметите, что хотя раскрывающиеся списки степени и темы создаются пять раз, запрос для каждого из них выполняется только один раз.

Я поместил запрос вне цикла for по простой причине: производительность. Нет необходимости выполнять один и тот же запрос пять раз, чтобы получить те же данные, поскольку это увеличивает нагрузку на сервер. mysql_data_seek() предпочтительным вариантом является выполнение запроса один раз, сохранение результирующего набора и использование функции mysql_data_seek() для итерации по нему столько раз, сколько необходимо. Это мелочь, но стоит отметить ее влияние на общую производительность приложений.

Раздел истории занятости использует таблицу «отрасль» для создания списка отраслей;

 <!-- employment history -->   <tr>  <td colspan=4><img src="images/em.gif"></td>  </tr>   <tr>  <td colspan=4><i>You may fill all or none of the sections below;  ensure that no fields are left empty per filled-in section</i></td>  </tr>   <?  // get industry list  $query = "SELECT id, industry from industry";  $ind_result = mysql_db_query($database, $query, $connection) or die  ("Error in query: $query. " . mysql_error());   for ($x=0; $x<3; $x++)  {  // if first time, print example  ?>   <tr>  <td>Employer [<? echo ($x+1); ?>]  <? if ($x == 0) { echo "<br><font size=-2>(example: ABC,  Inc.)</font>"; } ?> </td>  <td>Industry  <? if ($x == 0) { echo "<br><font size=-2>(example:  Advertising)</font>"; }  ?></td>  <td>Start year  <? if ($x == 0) { echo "<br><font size=-2>(example: 1996)</font>"; } ?>  </td>  <td>End year  <? if ($x == 0) { echo "<br><font size=-2>(example: 1998)</font>"; } ?>  </td>  </tr>   <tr>  <td><input type="text" name="employer[]" size="15"  maxlength="255"></td> <td><select name="industry[]"> <?  // print industry list  while (list($id, $industry) = mysql_fetch_row($ind_result))  {  echo "<option value=$id>$industry</option>";  }   // resultset pointer back to zero mysql_data_seek($ind_result, 0);  ?>  </select></td>  <td><input type="text" name="start_year[]" size="4" maxlength="4"></td>  <td><input type="text" name="end_year[]" size="4" maxlength="4"></td>  </tr>   <tr>  <td colspan=4>Responsibilities  <? if ($x == 0) { echo "<br><font size=-2>(example: Managing projects  and...)"; }?> </td>  </tr>   <tr>  <td colspan=4><textarea name="rsp[]" cols="40"  rows="8"></textarea></td> </tr>   <?  }  mysql_free_result($ind_result);  mysql_close($connection);  ?> 

В этом случае заявитель может подать до трех предыдущих работ; однако, поскольку я хочу, чтобы иллюстративный пример был напечатан только один раз, я включил небольшой оператор if в цикл for, чтобы проверить первую итерацию цикла.

Как указывалось ранее, для таких элементов, как история занятости и навыки, пользователь может ввести несколько записей; каждая из этих записей хранится как отдельная строка в соответствующей таблице, причем набор связан друг с другом через «избавленный» идентификатор возобновления. Также обратите внимание, что многие элементы из нескольких записей в приведенной выше форме передаются как массивы, а не как обычные переменные; это облегчает проверку их на недействительные данные.

Следует отметить, что я реализовал раздел навыков в виде последовательности текстовых полей, что позволяет пользователю свободно вводить все, что ему нравится. В качестве альтернативного подхода здесь можно было бы составить исчерпывающий список навыков в виде раскрывающегося списка и позволить пользователю выбирать из списка. Подход, который вы здесь применяете, повлияет на поисковые запросы, которые вы пишете для поисковой системы; Я буду обсуждать этот вопрос немного глубже, когда мы туда доберемся.

Вот как выглядит готовый продукт.

867image30

867image40

867image50

Copyright Melonfire , 2000. Все права защищены.

Время тестирования

Как только форма отправлена, сценарий apply_rslt.php вступает во владение. Функция этого сценария заключается в проверке данных, введенных в форму, путем обеспечения наличия всех необходимых полей в правильном формате и ввода этих данных в базу данных.

 <?  // apply_rslt.php - insert form data   // includes   // error checks   // open connection to database  $connection = mysql_connect($hostname, $user, $pass) or die ("Unable  to connect!");   // get job details  // use a join to get data from different tables  $query = "SELECT designation, jcode, department from listing,  department WHERE jcode = '$jcode' AND department.id =  listing.fk_department"; $result = mysql_db_query($database, $query,  $connection) or die ("Error in  query: $query. " . mysql_error());   // obtain data from resultset  list($designation, $jcode, $department) = mysql_fetch_row($result);   mysql_free_result($result);   // snip  ?> 

Как всегда, существуют обязательные проверки ошибок, чтобы убедиться, что код задания (переданный как скрытое значение из формы) является действительным.

Затем создается массив для хранения сообщений об ошибках, и различные необходимые текстовые поля проверяются. Если ошибки найдены, сообщения об ошибках добавляются в массив для последующего отображения.

 <?  // snip   // set up error list array  $errorList = array();  $count = 0;   // validate text input fields  if (empty($fname)) { $errorList[$count] = "Invalid entry: First  name"; $count++; }   if (empty($lname)) { $errorList[$count] = "Invalid entry: Last name";  $count++; }   // snip   if (empty($email) || isEmailInvalid($email)) { $errorList[$count] =  "Invalid entry: Email address"; $count++; }   // snip  ?> 

Функция empty() используется для проверки того, содержит ли переменная значение, а функции is_numeric() и is_string() используются для проверки, является ли значение числом или строкой. Как вы можете себе представить, эти встроенные функции очень удобны при тестировании на достоверность данных в форме.

Функция isEmailInvalid() — это пользовательская функция, написанная для проверки соответствия адреса электронной почты стандартному шаблону.

 <?  // check if email address is valid  function isEmailInvalid($val)  {  // regex for email validation  $pattern =  "/^([a-zA-Z0-9])+([.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-]+)  +/";   // match?  if(preg_match($pattern, $val))  {  return 0;  }  else  {  return 1;  }  }  ?> 

Мне также нужна проверка, чтобы убедиться, что пользователь еще не подал заявку на эту работу (это очень примитивная проверка, выполненная на основе адреса электронной почты пользователя).

 <?  // snip   // check to ensure that user has not already applied for same job  if (!empty($email))  {  $query = "SELECT email from r_user WHERE email = '$email' AND jcode =  '$jcode'";  $result = mysql_db_query($database, $query, $connection) or  die ("Error in  query: $query. " . mysql_error());  if (mysql_num_rows($result) > 0)  {  $errorList[$count] = "Duplicate entry: An  application for this job  already exists with the same email address";  $count++;  }  }   // snip  ?> 

Затем оцениваются различные многократные поля — образование, навыки, ссылки.

  <?  // snip   // validate multiple-record items  /*  1. get number of entries possible (rows)  2. check to see if any text field in that row is filled up  3. if yes, ensure that all other fields in that row  are also filled  4. if no, go to next row and repeat  */  
Дзен и искусство найма

Это может показаться не таким уж сложным, но управление отделом кадров компании на самом деле является довольно сложной задачей. Конечно, парням и девчонкам не нужно беспокоиться об утечках памяти, сборке мусора или искаженном коде; однако их проблемы часто столь же неотразимы и трудны, как и проблемы любого программиста или дизайнера интерфейса.

Одной из самых сложных проблем для любого отдела кадров является подбор персонала - искусство сопоставления потенциальных сотрудников с вакансиями в организации. Процесс сложен: разместите рекламу, подождите, пока не появятся резюме, а затем приступите к трудной работе по их поиску потенциальных новобранцев. За этим обычно следуют собеседования и проверки рекомендаций потенциальных кандидатов и, возможно, предложение работы.

Теперь, еще до того, как Интернет стал модным словом, единственный способ сообщить потенциальным сотрудникам о работе - через рекламу, либо в газете, либо в журнале, либо по телевидению. И как только отклики начнут поступать, у HR будет незавидная задача пробираться сквозь кучи бумаги, чтобы найти идеальное соответствие для открытой позиции. По ходу дела вы почти всегда могли ожидать, что потеряете или уничтожите несколько таких резюме, перенесете несколько сокращений бумаги и попрощаетесь с любой надеждой на социальную жизнь, которая у вас была.

Однако теперь, когда все и их глухие бабушки подключены к сети, связаться с потенциальными кандидатами стало намного проще. Теперь компания может либо рекламировать вакансии через онлайн-агентства по трудоустройству, либо размещать списки вакансий на своем веб-сайте. Так как агентства по трудоустройству онлайн не в восторге от своих сердец, последний вариант, как правило, тот, который предпочитают небольшие компании, хотя бы по экономическим причинам.

Конечно, создание рабочих мест на веб-сайте - это только верхушка айсберга. Как только эти резюме начинают поступать, все еще остается задача организовать их по категориям, перебрать все данные, чтобы составить короткий список кандидатов на собеседования, и подать отклоненные заявки для будущего использования. О да ... и просто для того, чтобы все было интересно, обычно на заднем плане тикают часы.

С помощью мощных инструментов с открытым исходным кодом, таких как PHP и MySQL, этот процесс может быть значительно упрощен. И в ходе этой статьи я собираюсь продемонстрировать, как, путем создания системы списков вакансий, подходящей для малого или среднего бизнеса.

Цель здесь двоякая: познакомить начинающих и промежуточных программистов с процессом разработки и реализации веб-приложения; и предложить менеджерам по персоналу и другим заинтересованным людям возможное решение их проблем.

Onwards!

Идеальный мир

Прежде чем приступить к изучению синтаксиса и структуры, важно понять проблемы, с которыми я сталкиваюсь, чтобы прояснить функциональные требования решения. Очень кратко, вот они:

  1. Каждое размещение звонка привлекает большое количество резюме. Большинство из этих резюме являются печатными документами, что затрудняет поиск среди них потенциальных кандидатов. В идеале резюме должны быть доступны в электронном виде.
  2. Резюме не в стандартном формате. Различные заявители используют разные шаблоны, стили и цвета, что затрудняет чтение и сортировку объема данных. В идеале каждое резюме должно быть в стандартном формате со стандартными полями и некоторой структурой, наложенной на данные внутри.
  3. С большим объемом ответов, заявления о приеме на работу могут оказаться неуместными или уничтоженными. В идеале, каждое резюме, введенное в систему, должно храниться там с любой потерей данных, чтобы к нему можно было получить доступ в любое время в будущем.
  4. Поиск в большом количестве заявлений о приеме на работу для определенных навыков или способностей занимает много времени. В идеале, база данных резюме должна легко найти поиск по заранее заданным критериям.

Поняв проблемы, становится легче определиться с требованиями решения. Анализ вышеуказанных проблем показывает, что большинство из них было бы решено, если бы у меня была система, которая:

  1. классифицировать резюме по работе;

  2. установил стандартную структуру содержания резюме;

  3. обработано резюме в электронном виде;

  4. встроенная поисковая система, позволяющая легко создавать подмножество базы данных, соответствующее определенным критериям

  5. архивированные заявки в течение определенного периода времени;

  6. позволил администраторам легко добавлять и удалять списки вакансий с веб-сайта.

Таким образом, это составляет начальный набор функций для приложения и служит руководством для будущей деятельности по разработке.

Входная точка

На этом этапе я также выдвинул несколько идей относительно того, как эта система может работать.

Концептуально я могу считать эту систему разделенной на две части: пользовательский раздел и административный раздел. Вот первоначальный проект основного потока и правил, которые я планирую встроить в это приложение.

  1. В пользовательском разделе точкой входа для потенциального соискателя будет список вакансий, в котором будет отображен список открытых вакансий в организации. Это, в свою очередь, должно позволить заявителю выбрать конкретную работу и получить подробную информацию об обязанностях, квалификации, зарплате и другую основную информацию.
  2. Кандидат также будет иметь возможность заполнить форму заявки в режиме онлайн и предоставить организации личную информацию, квалификацию, опыт и другую информацию, обычно находящуюся в резюме. Эти данные будут храниться в базе данных, доступной и доступной для поиска администраторами.
  3. В разделе администрирования администраторы будут иметь возможность добавлять, редактировать и удалять списки вакансий.
  4. Раздел администрирования также будет содержать форму поиска, чтобы помочь администраторам искать сохраненные приложения для определенных навыков или квалификации.

Copyright Melonfire , 2000. Все права защищены.

Переход к базе данных

Записав требования, становится намного легче приступить к проектированию архитектуры системы. Первая (и самая важная) часть этого процесса проектирования - это проектирование базы данных, в которой я буду разрабатывать таблицы для хранения данных приложения.

Сейчас самое время загрузить исходный код , чтобы вы могли обращаться к нему в этой статье (вам понадобится веб-сервер, поддерживающий PHP и базу данных MySQL).

Я планирую хранить списки вакансий в одной таблице списков.

 #  # Table structure for table 'listing'  #   DROP TABLE IF EXISTS listing;  CREATE TABLE listing (   jcode varchar(10) NOT NULL,   designation varchar(255) NOT NULL,   responsibilities text NOT NULL,   qualifications text NOT NULL,   cname varchar(255) NOT NULL,   cmail varchar(255) NOT NULL,   posted date DEFAULT '0000-00-00' NOT NULL,   fk_department tinyint(3) unsigned DEFAULT '0' NOT NULL,   fk_location tinyint(3) unsigned DEFAULT '0' NOT NULL,   fk_salary tinyint(3) unsigned DEFAULT '0' NOT NULL,   PRIMARY KEY (jcode),   KEY jcode (jcode)  );   #  # jcode - unique identifier for each job listing  # designation - job designation  # responsibilities - job responsibilities  # qualifications - job qualifications  # cname - name of person posting job  # cmail - email address of person posting job  # fk_department - foreign key - which department is this job in? #  fk_location - foreign key - which city is this job in? (for  multi-location organizations) # fk_salary - foreign key - what is the  expected compensation range for this job?  # 

Давайте заполним это парочкой фиктивных записей.

 #  # Dumping data for table 'listing'  #   INSERT INTO listing (jcode, designation, responsibilities,  qualifications, cname, cmail, posted, fk_department, fk_location,  fk_salary) VALUES ( 'X5436', 'Senior Web Developer', 'Applicant will  be responsible for developing Web applications and executing  Web-related projects for corporate customers. ', 'Applicant should be  familiar with scripting languages (PHP and Perl), databases (mySQL,  PostgreSQL). Applicant should be comfortable with both Windows and  *NIX operating system. Applicant will also be required to demonstrate  a thorough knowledge of software design and engineering principles.',  'Roger Rabbit', '[email protected]', '2001-05-22', '3', '4', '1');  INSERT INTO listing (jcode, designation, responsibilities, qualifications,  cname, cmail, posted, fk_department, fk_location, fk_salary) VALUES (  'KA6547', 'Project Manager', 'Applicant will be responsible for managing  projects within the organization. Responsibilities include developing  project plans and schedules, tracking project progress, communicating with  the customer, and ensuring that deadlines and deliveries are met.',  'Applicant should be familiar with office applications like Word, Excel,  Powerpoint and Project. Applicant should have prior experience  with project management tasks, and must bring enthusiasm  and professionalism to the post.', 'Bugs Bunny',  '[email protected]', '2001-04-05', '4', '5', '11'); 

Как вы можете видеть, эта таблица ссылается на информацию в трех других таблицах с помощью внешних ключей - давайте рассмотрим их далее, а также примеры записей для каждой из них.

 #  # Table structure for table 'location'  #   DROP TABLE IF EXISTS location;  CREATE TABLE location (   id tinyint(3) unsigned NOT NULL auto_increment,   location varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # location - name of city where applicant will be posted  #   # Dumping data for table 'location'  #   INSERT INTO location (id, location) VALUES ( '1', 'New York'); INSERT  INTO location (id, location) VALUES ( '2', 'London'); INSERT INTO  location (id, location) VALUES ( '3', 'Paris'); INSERT INTO location  (id, location) VALUES ( '4', 'Tokyo'); INSERT INTO location (id,  location) VALUES ( '5', 'Bombay');   #  # Table structure for table 'department'  #   DROP TABLE IF EXISTS department;  CREATE TABLE department (   id tinyint(3) unsigned NOT NULL auto_increment,   department varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # department - name of department  #   # Dumping data for table 'department'  #  INSERT INTO department (id, department) VALUES ( '1', 'Human  Resources'); INSERT INTO department (id, department) VALUES ( '2',  'Accounting'); INSERT INTO department (id, department) VALUES ( '3',  'Engineering'); INSERT INTO department (id, department) VALUES ( '4',  'Design'); INSERT INTO department (id, department) VALUES ( '5',  'Administration');   #  # Table structure for table 'salary'  #   DROP TABLE IF EXISTS salary;  CREATE TABLE salary (   id tinyint(3) unsigned NOT NULL auto_increment,   salary varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # salary - salary range  #   #  # Dumping data for table 'salary'   #  INSERT INTO salary (id, salary) VALUES ( '1', 'Not specified'); INSERT  INTO salary (id, salary) VALUES ( '2', '< USD 20,000'); INSERT INTO  salary (id, salary) VALUES ( '3', 'USD 20,000-29,900'); INSERT INTO  salary (id, salary) VALUES ( '4', 'USD 30,000-39,900'); INSERT INTO  salary (id, salary) VALUES ( '5', 'USD 40,000-49,900'); INSERT INTO  salary (id, salary) VALUES ( '6', 'USD 50,000-59,900'); INSERT INTO  salary (id, salary) VALUES ( '7', 'USD 60,000-69,900'); INSERT INTO  salary (id, salary) VALUES ( '8', 'USD 70,000-79,900'); INSERT INTO  salary (id, salary) VALUES ( '9', 'USD 80,000-89,900'); INSERT INTO  salary (id, salary) VALUES ( '10', 'USD 90,000-99,900'); INSERT INTO  salary (id, salary) VALUES ( '11', '> USD 100,000'); 

Это очень простые таблицы, каждая из которых имеет первичный ключ (id) и соответствующее значение. В случае, если вам интересно, почему я разбил эти элементы на отдельные таблицы, а не включил их все в таблицу «перечисления» или даже жестко запрограммировал их в конечном приложении, причина очень проста: я хочу сделать администратору проще добавлять и редактировать эти значения.

Разбивая их на отдельные таблицы, администратор, который хочет настроить приложение (например, изменить номер и название местоположения работы или отредактировать названия различных отделов), может сделать это без необходимости возиться с кодом программы. Это часть процесса, известного как «нормализация», и это очень важно при проектировании базы данных с двумя или более таблицами (ссылки на некоторые хорошие статьи по нормализации приведены в конце этой статьи).

Copyright Melonfire , 2000. Все права защищены.


Пять рупий

Четыре таблицы, которые вы видели выше, позволят мне построить страницы, на которых нужно перечислить открытые вакансии вместе с их описаниями. Однако, как только пользователь решил подать заявку на работу, данные, которые он вводит, должны где-то храниться. Следовательно, я создал несколько таблиц для хранения этой информации.

Обратите внимание, что информацию, которую я планирую запросить в форме заявления о приеме на работу, можно в целом разбить на пять разделов (личная информация, образование, история работы, навыки и ссылки), и поэтому таблицы, которые я построил, также соответствуют этой разбивке.

 #  # Table structure for table 'r_user'    DROP TABLE IF EXISTS r_user;  CREATE TABLE r_user (   rid tinyint(3) unsigned NOT NULL auto_increment,   jcode varchar(10) NOT NULL,   fname varchar(255) NOT NULL,   lname varchar(255) NOT NULL,   dob date DEFAULT '0000-00-00' NOT NULL,   addr1 varchar(255) NOT NULL,   addr2 varchar(255),   city varchar(255) NOT NULL,   state varchar(255) NOT NULL,   zip varchar(10) NOT NULL,   fk_country tinyint(3) unsigned DEFAULT '0' NOT NULL,   phone varchar(25) NOT NULL,   email varchar(255) NOT NULL,   url varchar(255),   relo tinyint(4) DEFAULT '0' NOT NULL,   posted date DEFAULT '0000-00-00' NOT NULL,   PRIMARY KEY (rid),   KEY jcode (jcode),   KEY rid (rid)  );   #  # rid - unique identifier for each application/resume, used to reference  it throughout the application  # jcode - job this application is for  # fname - applicant's first name  # lname - applicant's last name  # dob - applicant's date of birth  # addr1 - applicant's address  # add2 - applicant's address  # city - applicant's city  # state- applicant's state  # zip - applicant's zip code  # fk_country - applicant's country; foreign key to "country" table  # phone - applicant's phone number  # email - applicant's email address  # url - applicant's Web site  # relo - whether applicant is willing to relocate  # posted - date application was posted  # 

Таблица « r_user » содержит личную информацию заявителя и содержит одну запись на приложение; поле " rid " служит уникальным идентификатором для каждого приложения.

 #  # Table structure for table 'r_education'  #   DROP TABLE IF EXISTS r_education;  CREATE TABLE r_education (   rid tinyint(3) unsigned DEFAULT '0' NOT NULL,   institute varchar(255) NOT NULL,   fk_degree tinyint(3) unsigned DEFAULT '0' NOT NULL,   fk_subject tinyint(3) unsigned DEFAULT '0' NOT NULL,   year year(4) DEFAULT '0000' NOT NULL,   KEY fk_degree (fk_degree),   KEY fk_subject (fk_subject),   KEY rid (rid)  );   #  # rid - which application is this information for?  # institute - name of educational institution  # fk_institute - degree type; foreign key to "degree" table  # fk_subject - degree subject; foreign key to "subject" table # year -  degree obtained in which year? #   #  # Table structure for table 'r_employment'  #   DROP TABLE IF EXISTS r_employment;  CREATE TABLE r_employment (   rid tinyint(3) unsigned DEFAULT '0' NOT NULL,   employer varchar(255) NOT NULL,   fk_industry tinyint(3) unsigned DEFAULT '0' NOT NULL,   start_year year(4) DEFAULT '0000' NOT NULL,   end_year year(4) DEFAULT '0000' NOT NULL,   responsibilities text NOT NULL,   KEY rid (rid)  );   #  # rid - which application is this information for?  # employer - name of employer  # fk_industry - employer's industry; foreign key to "industry" table #  start_year - started work in...? # end_year - ended work in...?  # responsibilities - free-form description of job  responsibilities at this workplace  #   #  # Table structure for table 'r_skill'  #   DROP TABLE IF EXISTS r_skill;  CREATE TABLE r_skill (   rid tinyint(3) unsigned DEFAULT '0' NOT NULL,   skill varchar(255) NOT NULL,   experience tinyint(3) unsigned DEFAULT '0' NOT NULL,   KEY skill (skill),   KEY experience (experience),   KEY rid (rid)  );   #  # rid - which application is this information for?  # skill - name of skill  # experience - years experience in this skill  #   #  # Table structure for table 'r_reference'  #   DROP TABLE IF EXISTS r_reference;  CREATE TABLE r_reference (   rid tinyint(3) unsigned DEFAULT '0' NOT NULL,   name varchar(255) NOT NULL,   phone varchar(25) NOT NULL,   email varchar(255),   KEY rid (rid)  );   #  # rid - which application is this information for?  # name - reference's name  # phone - reference's phone number  # email - reference's email address  # 

r_education « r_education », « r_employer », « r_skills » и « r_reference » содержат образование, историю работы, навыки и ссылки соответственно. Обратите внимание, что эти таблицы могут содержать более одной записи для каждого кандидата (поскольку кандидат может перечислить несколько навыков или ссылок в одном приложении), причем записи связаны друг с другом через уникальное поле « rid ».

Copyright Melonfire , 2000. Все права защищены.


Счастливчик 13

Наконец, у меня есть несколько вспомогательных таблиц, в которых хранятся списки стран, отраслевые списки, тематические списки и тому подобное. Опять же, они были помещены в отдельные таблицы и связаны с помощью внешних ключей, чтобы упростить настройку приложения для различных нужд. Вот они:

 #  # Table structure for table 'country'  #   DROP TABLE IF EXISTS country;  CREATE TABLE country (   id tinyint(4) unsigned NOT NULL auto_increment,   country varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # country - country name  #   #  # Dumping data for table 'country'  #   INSERT INTO country (id, country) VALUES ( '', 'Afghanistan'); INSERT  INTO country (id, country) VALUES ( '', 'Albania');   #  # Table structure for table 'degree'  #   DROP TABLE IF EXISTS degree;  CREATE TABLE degree (   id tinyint(3) unsigned NOT NULL auto_increment,   degree varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # degree - degree type  #   #  # Dumping data for table 'degree'  #   INSERT INTO degree (id, degree) VALUES ( '1', 'High School degree');  INSERT INTO degree (id, degree) VALUES ( '2', 'Undergraduate degree');  INSERT INTO degree (id, degree) VALUES ( '3', 'Bachelor's degree');   #  # Table structure for table 'industry'  #    DROP TABLE IF EXISTS industry;  CREATE TABLE industry (   id tinyint(4) unsigned NOT NULL auto_increment,   industry varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # industry - industry type  #   #  # Dumping data for table 'industry'  #   INSERT INTO industry (id, industry) VALUES ( '1', 'Advertising');  INSERT INTO industry (id, industry) VALUES ( '2', 'Agriculture and  Forestry');   #  # Table structure for table 'subject'  #   DROP TABLE IF EXISTS subject;  CREATE TABLE subject (   id tinyint(3) unsigned NOT NULL auto_increment,   subject varchar(255) NOT NULL,   PRIMARY KEY (id)  );   #  # id - unique record identifier  # subject - subject name  #    #  # Dumping data for table 'subject'  #   INSERT INTO subject (id, subject) VALUES ( '', 'Accounting'); INSERT  INTO subject (id, subject) VALUES ( '', 'Actuarial Science'); 

Файл "jobs.sql" в архиве исходного кода содержит более длинный список элементов для этих вспомогательных таблиц.

Сколько это столов? Ну, Джо, это счастливый номер тринадцать! Гулянка!

Copyright Melonfire , 2000. Все права защищены.


Строим фундамент

С разработанной базой данных, пришло время написать код для взаимодействия с ней. Первый скрипт, job_list.php, является точкой входа в приложение и просто отображает список доступных заданий, классифицированных по отделам.

 <?  // job_list.php - display list of open jobs   // includes  include("config.php");  include("functions.php");  ?>   <html>  <head>  <basefont face="Verdana" size="2">  </head>   <body bgcolor=white>   <? $image="listings.jpg"; ?>  <? include("header.inc.php"); ?>   <?  // generate list  ?>   <? include("footer.inc.php"); ?>   </body>  </html> 

Каждая страница, созданная с помощью этого приложения, имеет определенный макет - синий баннер вверху, логотип справа и заголовок (на самом деле изображение). Внизу каждой страницы есть уведомление об авторских правах и отказ от ответственности. Поскольку эти элементы останутся постоянными, через приложение я поместил соответствующий HTML-код в отдельные файлы верхнего и нижнего колонтитула и просто include() d их на каждой странице.

Опять же, разделив общие элементы интерфейса на отдельные файлы, я упростил настройку внешнего вида приложения; просто измените эти файлы, и изменения будут отражены на всех страницах.

Переменная $image хранит имя заголовка изображения для каждой страницы и используется "header.inc.php" - как вы можете видеть.

 <!-- appears at the top of every page -->  <table bgcolor="6583C3" width="100%" cellspacing="0" cellpadding="0">  <tr> <td height=50 align=right>&nbsp;<img src="images/header.gif"  alt="" border="0"></td>  </tr>  </table>  <p>  <img src="images/<? echo $image; ?>" alt="" border="0">  <p>  <!-- end of header.inc --> 

Кроме того, два других файла, «config.php» и «functions.php», также включены вверху каждой страницы - они хранят информацию о доступе к базе данных и полезные функции, соответственно.

 <?  // config.php - useful variables   // database parameters  // alter this as per your configuration  $database="jobs";  $user = "root";  $pass = "";  $hostname = "localhost";   ?> 

Вернуться к «job_list.php» - вот код, который заботится о подключении к базе данных и фактически генерирует список вакансий.

 <? include("header.inc.php"); ?>   <?   // open connection to database  $connection = mysql_connect($hostname, $user, $pass) or die ("Unable  to connect!");   // get list of departments with open jobs  $query = "SELECT DISTINCT id, department from department, listing  WHERE department.id = listing.fk_department"; $result =  mysql_db_query($database, $query, $connection) or die ("Error in  query: $query. " . mysql_error());   // iterate through resultset  while(list($id, $department) = mysql_fetch_row($result))  {   // print department name  echo "<b>Department:</b> $department";   // look for jobs within the department and print as list  $query2 = "SELECT jcode, designation from listing WHERE  listing.fk_department = '$id'";  $result2 = mysql_db_query($database, $query2, $connection) or die  ("Error in query: $query2. " . mysql_error());   echo "<ul>";  while(list($jcode, $dsg) = mysql_fetch_row($result2))  {  echo "<li><a href=job_details.php?jcode=$jcode>$dsg  ($jcode)</a>";  }  echo "</ul>";   echo "<p>";  }   // clean up  mysql_close($connection);  ?>   <? include("footer.inc.php"); ?> 

В этом случае я сначала запросил таблицу «листинга» для списка отделов, в которых есть открытые вакансии - обратите внимание на ключевое слово DISTINCT чтобы исключить дублирующиеся записи. Затем для каждого из этих отделов я распечатал обозначение должности и код задания и связал его со сценарием, который будет отображать дополнительную информацию.

Вот как это выглядит:

867image10

Copyright Melonfire , 2000. Все права защищены.


Дьявол кроется в деталях

Сценарий "job_details.php" предназначен для принятия конкретного кода работы, подключения к базе данных и печати таких сведений, как квалификация и обязанности для этой работы. Он также содержит ссылку на форму заявления о приеме на работу, если пользователь заинтересован в подаче заявления на работу.

 <?  // job_details.php - display job details   // includes   // check for missing parameters  if (!$jcode || $jcode == "")  {  header("Location:error.php");  exit;  }   // open connection to database  $connection = mysql_connect($hostname, $user, $pass) or die ("Unable  to connect!");   // get job details  // use a join to get data from different tables  $query = "SELECT listing.designation, listing.jcode,  department.department, location.location, salary.salary,  listing.responsibilities, listing.qualifications, listing.cname,  listing.cmail, listing.posted from department, listing, location,  salary WHERE department.id = listing.fk_department AND location.id =  listing.fk_location AND salary.id =  listing.fk_salary AND listing.jcode = '$jcode'";  $result = mysql_db_query($database, $query, $connection) or die ("Error in  query: $query. " . mysql_error());   // error check  if (mysql_num_rows($result) <= 0)  {  header("Location:error.php");  exit;  }  else  {  // obtain data from resultset  list($designation, $jcode, $department, $location, $salary,  $description, $qualification, $cname, $cmail, $posted) =  mysql_fetch_row($result);   // clean up  mysql_close($connection);  ?>   <!-- snip -->   <!-- print job details -->  <b>Designation:</b> <? echo $designation; ?>  <p>  <b>Department:</b> <? echo $department; ?>  <p>   <!-- snip -->    <b>Posted on:</b> <? echo fixDate($posted); ?>  <p>  <!-- link to application form -->  <a href="apply.php?jcode=<? echo $jcode; ?>">Apply online</a> for this  job, or <a href="job_list.php">return to job listings</a>   <!-- snip -->   <?  }  ?> 

Первое, что делает этот скрипт, это проверяет, что ему был передан код задания, через метод URL GET . Если этот код задания отсутствует, управление передается универсальному обработчику ошибок через перенаправление HTTP и функцию header() .

Предполагая, что код задания присутствует, следующее, что нужно сделать, - убедиться, что он действителен и что такое задание существует в базе данных. Запрос выполняется для получения полного описания задания (путем соединения таблицы «листинга» с другими вспомогательными таблицами через внешние ключи). Если запрос возвращает значение, информация печатается; если нет, обработчик ошибок вызывается снова.

Функция list() используется для разделения различных элементов возвращаемой строки и назначения их обычным переменным; затем они печатаются в соответствующих местах. В конце ссылка на скрипт «apply.php» переводит пользователя в форму заявки, снова используя код задания в качестве идентификатора.

Обратите внимание на fixDate() - она ​​используется для превращения типа DATE в MySQL во что-то более читаемое и читается из «functions.php».

 <?  // function to format mySQL DATE values  function fixDate($val)  {  //split it up into components  $arr = explode(" ", $val);  $datearr = explode("-", $arr[0]);  // create a timestamp with mktime(), format it with date() return  date("d MY", mktime(0, 0, 0, $datearr[1], $datearr[2], $datearr[0]));  }  ?> 

Вот как это выглядит:

867image20

Copyright Melonfire , 2000. Все права защищены.


Применяя себя

Форма заявления о работе "apply.php" и соответствующий обработчик данных "apply_rslt.php" составляют ядро ​​этого приложения. Они отвечают за генерацию необработанных данных и их надлежащее хранение в базе данных; следовательно, особое внимание должно быть уделено их разработке.

Как и предыдущий скрипт, «apply.php» должен сначала проверить, чтобы убедиться, что он получил действительный код задания.

 <?  // apply.php - generate application form   // includes   // error checks   // open connection to database  $connection = mysql_connect($hostname, $user, $pass) or die ("Unable  to connect!");   // get job details  // use a join to get data from different tables  $query = "SELECT designation, jcode, department from listing,  department WHERE jcode = '$jcode' AND department.id =  listing.fk_department"; $result = mysql_db_query($database, $query,  $connection) or die ("Error in  query: $query. " . mysql_error());   // error check  if (mysql_num_rows($result) <= 0)  {  header("Location:error.php");  exit;  }  else  {  // obtain data from resultset  list($designation, $jcode, $department) = mysql_fetch_row($result);   mysql_free_result($result);  ?> 

Остальная часть сценария является обычной формой; он включает в себя как текстовые поля, так и раскрывающиеся списки выбора, многие из которых генерируются из вспомогательных таблиц, обсуждавшихся ранее. Взгляните на раздел личной информации.

  <table border="0" cellspacing="5" cellpadding="2">  <form action="apply_rslt.php" method="post">  <input type="hidden" name="jcode" value="<? echo $jcode; ?>"   <!-- personal information section -->   <tr>  <td colspan=4><img src="images/pi.gif"></td>  </tr>   <tr>  <td colspan=2>First name<font color="red">*</font></td>  <td colspan=2>Last name<font color="red">*</font></td>  </tr>   <tr>  <td colspan=2><input type="text" name="fname" size="20"  maxlength="255"></td> <td colspan=2><input type="text" name="lname"  size="20" maxlength="255"></td>  </tr>   <!-- snip -->   <tr>  <td colspan=4>Country<font color="red">*</font></td>  </tr>   <tr>  <td colspan=4><select name="country">  <?  // get country list  $query = "SELECT id, country from country";  $result = mysql_db_query($database, $query, $connection) or die  ("Error in  query: $query. " . mysql_error());  while (list($id, $country) = mysql_fetch_row($result))  {  echo "<option value=$id>$country</option>";  }  mysql_free_result($result);  ?>  </select></td>  </tr>   <!-- snip -->   <tr>  <td colspan=4>Date of birth<font color="red">*</font><br><font  size="-2">(in dd-mm-yyyy format)</font></td> </tr>   <tr>  <td colspan=4>  <select name="dd">  <? for ($x=1; $x<=31; $x++) { echo "<option value="" .  sprintf("%02d", $x) . "">" . sprintf("%02d", $x) . "</option>";  } ?>  </select> -  <select name="mm">  <? for ($x=1; $x<=12; $x++) { echo "<option value="" .  sprintf("%02d", $x)  . "">" . sprintf("%02d", $x) . "</option>";  } ?>  </select> -  <select name="yyyy">  <!-- display from 1940 to (current year-10) -->  <? for ($x=1940; $x<=(date("Y", mktime())-10); $x++) { echo "<option  value=$x>$x</option>"; } ?>  </select>  </td>  </tr> 

Как вы можете видеть, список выбора страны генерируется из таблицы "страна" в базе данных, а раскрывающиеся списки даты рождения создаются с помощью циклов "for".

Раздел «Образование» использует таблицы «степень» и «предмет» для составления списка возможных образовательных квалификаций.

  <!-- education section -->  <tr>  <td colspan=4><img src="images/ed.gif"></td>  </tr>   <tr>  <td colspan=4><i>You may fill all or none of the rows below; ensure  that no fields are left empty per filled-in row</i></td>  </tr>   <tr>  <td>Institute/University<br><font size=-2>(example: XYZ  University)</td> <td>Degree<br><font size=-2>(example: Master's  degree)</td> <td>Primary subject<br><font size=-2>(example:  Accounting)</td> <td>Year<br><font size=-2>(example: 1992)</td> </tr>   <?  // get degree list  $query = "SELECT id, degree from degree";  $degree_result = mysql_db_query($database, $query, $connection) or die  ("Error in query: $query. " . mysql_error());   // get subject list  $query = "SELECT id, subject from subject";  $subject_result = mysql_db_query($database, $query, $connection) or  die ("Error in query: $query. " . mysql_error());   for ($x=0; $x<5; $x++)  {  ?>  <tr>  <td><input type="text" name="institute[]" size="20"  maxlength="255"></td> <td><select name="degree[]"> <?  while (list($id, $degree) = mysql_fetch_row($degree_result))  {  echo "<option value=$id>$degree</option>";  }  // same data, no need to query again  mysql_data_seek($degree_result, 0);  ?>  </select></td>  <td><select name="subject[]">  <?   while (list($id, $subject) = mysql_fetch_row($subject_result))  {  echo "<option value=$id>$subject</option>";  }  // same data, no need to query again mysql_data_seek($subject_result,  0); ?>  </select></td>  <td><input type="text" name="degree_year[]" size="4" maxlength="4"></td>  </tr>   <?  }  mysql_free_result($degree_result);  mysql_free_result($subject_result);  ?> 

Как видите, приведенный выше код сгенерирует пять строк, чтобы кандидат мог ввести до пяти различных квалификаций. Вы заметите, что хотя раскрывающиеся списки степени и темы создаются пять раз, запрос для каждого из них выполняется только один раз.

Я поместил запрос вне цикла for по простой причине: производительность. Нет необходимости выполнять один и тот же запрос пять раз, чтобы получить те же данные, поскольку это увеличивает нагрузку на сервер. mysql_data_seek() предпочтительным вариантом является выполнение запроса один раз, сохранение результирующего набора и использование функции mysql_data_seek() для итерации по нему столько раз, сколько необходимо. Это мелочь, но стоит отметить ее влияние на общую производительность приложений.

Раздел истории занятости использует таблицу «отрасль» для создания списка отраслей;

 <!-- employment history -->   <tr>  <td colspan=4><img src="images/em.gif"></td>  </tr>   <tr>  <td colspan=4><i>You may fill all or none of the sections below;  ensure that no fields are left empty per filled-in section</i></td>  </tr>   <?  // get industry list  $query = "SELECT id, industry from industry";  $ind_result = mysql_db_query($database, $query, $connection) or die  ("Error in query: $query. " . mysql_error());   for ($x=0; $x<3; $x++)  {  // if first time, print example  ?>   <tr>  <td>Employer [<? echo ($x+1); ?>]  <? if ($x == 0) { echo "<br><font size=-2>(example: ABC,  Inc.)</font>"; } ?> </td>  <td>Industry  <? if ($x == 0) { echo "<br><font size=-2>(example:  Advertising)</font>"; }  ?></td>  <td>Start year  <? if ($x == 0) { echo "<br><font size=-2>(example: 1996)</font>"; } ?>  </td>  <td>End year  <? if ($x == 0) { echo "<br><font size=-2>(example: 1998)</font>"; } ?>  </td>  </tr>   <tr>  <td><input type="text" name="employer[]" size="15"  maxlength="255"></td> <td><select name="industry[]"> <?  // print industry list  while (list($id, $industry) = mysql_fetch_row($ind_result))  {  echo "<option value=$id>$industry</option>";  }   // resultset pointer back to zero mysql_data_seek($ind_result, 0);  ?>  </select></td>  <td><input type="text" name="start_year[]" size="4" maxlength="4"></td>  <td><input type="text" name="end_year[]" size="4" maxlength="4"></td>  </tr>   <tr>  <td colspan=4>Responsibilities  <? if ($x == 0) { echo "<br><font size=-2>(example: Managing projects  and...)"; }?> </td>  </tr>   <tr>  <td colspan=4><textarea name="rsp[]" cols="40"  rows="8"></textarea></td> </tr>   <?  }  mysql_free_result($ind_result);  mysql_close($connection);  ?> 

В этом случае заявитель может подать до трех предыдущих работ; однако, поскольку я хочу, чтобы иллюстративный пример был напечатан только один раз, я включил небольшой оператор if в цикл for, чтобы проверить первую итерацию цикла.

Как указывалось ранее, для таких элементов, как история занятости и навыки, пользователь может ввести несколько записей; каждая из этих записей хранится как отдельная строка в соответствующей таблице, причем набор связан друг с другом через «избавленный» идентификатор возобновления. Также обратите внимание, что многие элементы из нескольких записей в приведенной выше форме передаются как массивы, а не как обычные переменные; это облегчает проверку их на недействительные данные.

Следует отметить, что я реализовал раздел навыков в виде последовательности текстовых полей, что позволяет пользователю свободно вводить все, что ему нравится. В качестве альтернативного подхода здесь можно было бы составить исчерпывающий список навыков в виде раскрывающегося списка и позволить пользователю выбирать из списка. Подход, который вы здесь применяете, повлияет на поисковые запросы, которые вы пишете для поисковой системы; Я буду обсуждать этот вопрос немного глубже, когда мы туда доберемся.

Вот как выглядит готовый продукт.

867image30

867image40

867image50

Copyright Melonfire , 2000. Все права защищены.


Время тестирования

Как только форма отправлена, сценарий apply_rslt.php вступает во владение. Функция этого сценария заключается в проверке данных, введенных в форму, путем обеспечения наличия всех необходимых полей в правильном формате и ввода этих данных в базу данных.

 <?  // apply_rslt.php - insert form data   // includes   // error checks   // open connection to database  $connection = mysql_connect($hostname, $user, $pass) or die ("Unable  to connect!");   // get job details  // use a join to get data from different tables  $query = "SELECT designation, jcode, department from listing,  department WHERE jcode = '$jcode' AND department.id =  listing.fk_department"; $result = mysql_db_query($database, $query,  $connection) or die ("Error in  query: $query. " . mysql_error());   // obtain data from resultset  list($designation, $jcode, $department) = mysql_fetch_row($result);   mysql_free_result($result);   // snip  ?> 

Как всегда, существуют обязательные проверки ошибок, чтобы убедиться, что код задания (переданный как скрытое значение из формы) является действительным.

Затем создается массив для хранения сообщений об ошибках, и различные необходимые текстовые поля проверяются. Если ошибки найдены, сообщения об ошибках добавляются в массив для последующего отображения.

 <?  // snip   // set up error list array  $errorList = array();  $count = 0;   // validate text input fields  if (empty($fname)) { $errorList[$count] = "Invalid entry: First  name"; $count++; }   if (empty($lname)) { $errorList[$count] = "Invalid entry: Last name";  $count++; }   // snip   if (empty($email) || isEmailInvalid($email)) { $errorList[$count] =  "Invalid entry: Email address"; $count++; }   // snip  ?> 

Функция empty() используется для проверки того, содержит ли переменная значение, а функции is_numeric() и is_string() используются для проверки, является ли значение числом или строкой. Как вы можете себе представить, эти встроенные функции очень удобны при тестировании на достоверность данных в форме.

Функция isEmailInvalid() - это пользовательская функция, написанная для проверки соответствия адреса электронной почты стандартному шаблону.

 <?  // check if email address is valid  function isEmailInvalid($val)  {  // regex for email validation  $pattern =  "/^([a-zA-Z0-9])+([.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-]+)  +/";   // match?  if(preg_match($pattern, $val))  {  return 0;  }  else  {  return 1;  }  }  ?> 

Мне также нужна проверка, чтобы убедиться, что пользователь еще не подал заявку на эту работу (это очень примитивная проверка, выполненная на основе адреса электронной почты пользователя).

 <?  // snip   // check to ensure that user has not already applied for same job  if (!empty($email))  {  $query = "SELECT email from r_user WHERE email = '$email' AND jcode =  '$jcode'";  $result = mysql_db_query($database, $query, $connection) or  die ("Error in  query: $query. " . mysql_error());  if (mysql_num_rows($result) > 0)  {  $errorList[$count] = "Duplicate entry: An  application for this job  already exists with the same email address";  $count++;  }  }   // snip  ?> 

Затем оцениваются различные многократные поля - образование, навыки, ссылки.

  <?  // snip   // validate multiple-record items  /*  1. get number of entries possible (rows)  2. check to see if any text field in that row is filled up  3. if yes, ensure that all other fields in that row  are also filled  4. if no, go to next row and repeat  */   // check education listings  for ($x=0; $x<sizeof($institute); $x++)  {  if(!empty($institute[$x]) || !empty($degree_year[$x]))  {  if(empty($degree[$x]) || empty($degree_year[$x]) ||  !is_numeric($degree_year[$x]))  {  $errorList[$count] = "Invalid entry:  Educational qualifications, item "  . ($x+1);  $count++;  }  }  }   // similar checks for employment, skills and references   // snip  ?> 

Во время разработки различные проверки ошибок могут показаться утомительными; однако, они, безусловно, самая важная часть этого сценария. Если данные, введенные в форму, не проверены должным образом, вы начнете видеть недействительные или неполные данные в вашей базе данных; это влияет на целостность ваших структур данных и эффективность ваших поисковых запросов.

Сделайте свои процедуры проверки как можно более строгими и постарайтесь охватить все ваши базы. Недостаточное внимание к этому может привести к бессонным ночам и головным болям. И после того, как вы закончите писать их, дайте вашему дружелюбному хакеру по соседству несколько долларов и посмотрите, сможет ли он пройти мимо них.

Copyright Melonfire , 2000. Все права защищены.


Подача всего этого

В конце всей проверки проверяется $errorList массива $errorList . Если размер равен 0, это означает, что ошибок не обнаружено, и начинается вставка базы данных.

 <?  // no errors  if (sizeof($errorList) == 0)  {  // insert personal info  $query = "INSERT INTO r_user (jcode, fname, lname, dob, addr1, addr2,  city, state, zip, fk_country, phone, email, url, relo, posted) VALUES  ('$jcode', '$fname', '$lname', '$dob', '$addr1', '$addr2', '$city',  '$state', '$zip', '$country', '$phone', '$email', '$url',  '$relo', NOW(''))";  $result = mysql_db_query($database, $query, $connection) or  die ("Error in  query: $query. " . mysql_error());   // get resume id, for use in subsequent operations  $rid = mysql_insert_id($connection);   // insert educational qualifications  for($x=0; $x<sizeof($institute); $x++)  {  if (!empty($institute[$x]) &&  !empty($degree_year[$x]))  {  $query = "INSERT INTO r_education (rid,  institute, fk_degree,  fk_subject, year) VALUES ('$rid', '$institute[$x]', '$degree[$x]',  '$subject[$x]', '$degree_year[$x]')";  $result = mysql_db_query($database, $query,  $connection) or die ("Error  in query: $query. " . mysql_error());  }  }   // and so on   // print success code  echo "Your application has been accepted.<p><a  href=job_list.php>Return to job listings</a>";  }  else  {  // or list errors  listErrors();  }  ?> 

Если присутствуют сообщения об ошибках, listErrors() функция listErrors() для отображения списка сообщений об ошибках. Вставка базы данных не происходит, и пользователь имеет возможность вернуться на предыдущую страницу, чтобы исправить ошибки.

 <?  // produce a list of errors after validating a form  function listErrors()  {  // read the errorList array  global $errorList;   // print as list  echo "The following errors were encountered: <br>";  echo "<ul>";  for ($x=0; $x<sizeof($errorList); $x++)  {  echo "<li>$errorList[$x]";  }  echo "</ul>";   // link to go back and correct errors  echo "Click <a href=javascript:history.back();>here</a> to go back to  the previous page and correct the errors"; }  ?> 

На данный момент вся соответствующая информация о пользователях была сохранена в различных таблицах базы данных. Это конец потока пользовательского процесса; в настоящее время акцент смещается на поиск и обслуживание данных, которые являются исключительно административными функциями.

Во второй части этой статьи я рассмотрю сценарии администрирования, связанные с добавлением, редактированием и удалением списков вакансий, а также рассмотрю основную поисковую систему, которая просматривает все данные. А пока скачайте код, поиграйте с ним, пришлите мне свои мысли / пламя / сбережения ... и возвращайтесь в следующий раз, чтобы узнать больше!

Примечание. Все примеры в этой статье были протестированы на Linux / i586 с Apache 1.3.12, mySQL 3.23 и PHP 4.02. Примеры являются только иллюстративными и не предназначены для производственной среды. YMMV!

>

// проверить списки образования
для ($ x = 0; $ x <sizeof ($ institute); $ x ++)
{
if (! empty ($ institute [$ x]) ||! empty ($ deg_year [$ x]))
{
if (пусто ($ степень [$ x]) || пусто ($ deg_year [$ x]) ||
! Is_numeric ($ degree_year [$ х]))
{
$ errorList [$ count] = "Неверная запись:
Образовательные квалификации, пункт "
, ($ Х + 1);
$ Количество ++;
}
}
}

// похожие проверки на трудоустройство, навыки и рекомендации

// снип
?>

Во время разработки различные проверки ошибок могут показаться утомительными; однако, они, безусловно, самая важная часть этого сценария. Если данные, введенные в форму, не проверены должным образом, вы начнете видеть недействительные или неполные данные в вашей базе данных; это влияет на целостность ваших структур данных и эффективность ваших поисковых запросов.

Сделайте свои процедуры проверки как можно более строгими и постарайтесь охватить все ваши базы. Недостаточное внимание к этому может привести к бессонным ночам и головным болям. И после того, как вы закончите писать их, дайте вашему дружелюбному хакеру по соседству несколько долларов и посмотрите, сможет ли он пройти мимо них.

Copyright Melonfire , 2000. Все права защищены.


Подача всего этого

В конце всей проверки проверяется $errorList массива $errorList . Если размер равен 0, это означает, что ошибок не обнаружено, и начинается вставка базы данных.

 <?  // no errors  if (sizeof($errorList) == 0)  {  // insert personal info  $query = "INSERT INTO r_user (jcode, fname, lname, dob, addr1, addr2,  city, state, zip, fk_country, phone, email, url, relo, posted) VALUES  ('$jcode', '$fname', '$lname', '$dob', '$addr1', '$addr2', '$city',  '$state', '$zip', '$country', '$phone', '$email', '$url',  '$relo', NOW(''))";  $result = mysql_db_query($database, $query, $connection) or  die ("Error in  query: $query. " . mysql_error());   // get resume id, for use in subsequent operations  $rid = mysql_insert_id($connection);   // insert educational qualifications  for($x=0; $x<sizeof($institute); $x++)  {  if (!empty($institute[$x]) &&  !empty($degree_year[$x]))  {  $query = "INSERT INTO r_education (rid,  institute, fk_degree,  fk_subject, year) VALUES ('$rid', '$institute[$x]', '$degree[$x]',  '$subject[$x]', '$degree_year[$x]')";  $result = mysql_db_query($database, $query,  $connection) or die ("Error  in query: $query. " . mysql_error());  }  }   // and so on   // print success code  echo "Your application has been accepted.<p><a  href=job_list.php>Return to job listings</a>";  }  else  {  // or list errors  listErrors();  }  ?> 

Если присутствуют сообщения об ошибках, listErrors() функция listErrors() для отображения списка сообщений об ошибках. Вставка базы данных не происходит, и пользователь имеет возможность вернуться на предыдущую страницу, чтобы исправить ошибки.

 <?  // produce a list of errors after validating a form  function listErrors()  {  // read the errorList array  global $errorList;   // print as list  echo "The following errors were encountered: <br>";  echo "<ul>";  for ($x=0; $x<sizeof($errorList); $x++)  {  echo "<li>$errorList[$x]";  }  echo "</ul>";   // link to go back and correct errors  echo "Click <a href=javascript:history.back();>here</a> to go back to  the previous page and correct the errors"; }  ?> 

На данный момент вся соответствующая информация о пользователях была сохранена в различных таблицах базы данных. Это конец потока пользовательского процесса; в настоящее время акцент смещается на поиск и обслуживание данных, которые являются исключительно административными функциями.

Во второй части этой статьи я рассмотрю сценарии администрирования, связанные с добавлением, редактированием и удалением списков вакансий, а также рассмотрю основную поисковую систему, которая просматривает все данные. А пока скачайте код, поиграйте с ним, пришлите мне свои мысли / пламя / сбережения ... и возвращайтесь в следующий раз, чтобы узнать больше!

Примечание. Все примеры в этой статье были протестированы на Linux / i586 с Apache 1.3.12, mySQL 3.23 и PHP 4.02. Примеры являются только иллюстративными и не предназначены для производственной среды. YMMV!